Compare commits

..

606 Commits

Author SHA1 Message Date
Spring Builds 73855ec6f0 Release v6.2.0-M2 2024-05-16 09:46:53 +00:00
Juergen Hoeller 28398624bf Avoid cyclic package import for javadoc reference 2024-05-15 22:19:20 +02:00
rstoyanchev da3b59e7a9 Merge branch '6.1.x' 2024-05-15 20:07:13 +01:00
rstoyanchev edb6bb717d Add UrlHandlerFilter
Closes gh-31366
2024-05-15 18:45:07 +01:00
rstoyanchev 48ad7b1bcd Remove unused code in WebHttpHandlerBuilder
The code was necessary while ForwardedHeaderFilter was deprecated
but still possible to use. When the filter was removed, this code
could have been removed as well.
2024-05-15 18:45:07 +01:00
SharadK10 a3b62a80bc Improve wording in Spring Framework Overview documentation
Closes gh-32829
2024-05-15 19:00:27 +02:00
Sam Brannen 6cdb34410b Register DynamicPropertyRegistry as singleton bean in test ApplicationContext
Prior to this commit, DynamicPropertyRegistry could only be used with a
static @⁠DynamicPropertySource method in an integration test class;
however, it can also be useful to be able to register a "dynamic
property" from within a test's ApplicationContext -- for example, in a
@⁠Bean method in a @⁠Configuration class that is specific to testing
scenarios.

To support such use cases, this commit updates the dynamic property
source infrastructure so that a DynamicPropertyRegistry is always
registered as a singleton bean in a test's ApplicationContext. This
allows DynamicPropertyRegistry to be autowired into a @⁠Configuration
class or supplied to a @⁠Bean method as an argument as shown in the
following example.

@⁠Bean
@⁠DynamicPropertySource
ApiServer apiServer(DynamicPropertyRegistry registry) {
	ApiServer apiServer = new ApiServer();
	registry.add("api.url", apiServer::getUrl);
	return apiServer;
}

Note that the use of @⁠DynamicPropertySource on the @⁠Bean method is
optional and results in the corresponding bean being eagerly
initialized so that other singleton beans in the context can be given
access to the dynamic properties sourced from that bean when those
other beans are initialized.

Side note: DynamicPropertySourceBeanInitializer temporarily implements
LoadTimeWeaverAware since doing so is currently the only way to have a
component eagerly initialized before the
ConfigurableListableBeanFactory.preInstantiateSingletons() phase.
However, we plan to introduce a first-class callback to support such
use cases in the future.

Closes gh-32271
2024-05-15 17:09:03 +02:00
Juergen Hoeller 7d8279afec Merge branch '6.1.x' 2024-05-15 14:16:51 +02:00
Sam Brannen d625b3de27 Document SpEL IndexAccessor support in the reference manual
Closes gh-32735
2024-05-15 11:47:42 +02:00
Simon Baslé 531da015e1 Improve MediaTypeAssert to-string-equality assertions
This commit introduces a `isNotEqualTo(String)` assertion in order to
avoid false negatives when using the default Object-based assertion.

This is a risk since MediaTypeAssert has a `isEqualTo(String)` method
that overrides the base object method and parses the provided String
into a MediaType. Users may thus be tempted to use the reverse assertion
and expect the same parsing behaviour.

This commit also adds tests around the String parsing and the isNotEqual
cases.

Closes gh-32756
2024-05-15 11:14:13 +02:00
Juergen Hoeller 60c5f44e1d Upgrade reactor.util.annotation content
See gh-32785
2024-05-14 22:59:43 +02:00
Juergen Hoeller c69ab069b7 Upgrade to Reactor 2024.0.0-M2
Closes gh-32785
2024-05-14 22:03:38 +02:00
Juergen Hoeller 7c534eeeeb Merge branch '6.1.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2024-05-14 22:02:32 +02:00
Sam Brannen 716e7de841 Simplify implementation of SpelNodeImpl.loadClassForExitDescriptor(...) 2024-05-14 17:58:05 +02:00
Brian Clozel f7a6a7b814 Allow ReflectionHints to register hints on interface hierarchies
This commit promotes a previously private method in
`BeanRegistrationsAotContribution` to a top-level method in
`ReflectionHints`.

This helps to register hints on all interfaces implemented in the class
hierarchy of the given type.

Closes gh-32824
2024-05-14 17:54:12 +02:00
Simon Baslé a86612a254 Use by-type semantics in bean overriding if no explicit name is provided
This change switches default behavior of `@TestBean`, `@MockitoBean` and
`@MockitoSpyBean` to match the bean definition / bean to override by
type in the case there is no explicit bean name provided via the
annotation. The previous behavior of using the annotated field's name
is still an option for implementors, but no longer the default.

Closes gh-32761
2024-05-14 16:42:11 +02:00
Sam Brannen fc07946e60 Polishing 2024-05-14 15:03:25 +02:00
Sam Brannen 061c13d367 Support compilation of varargs invocations in SpEL for array subtypes
This commit merges support for compiling SpEL expressions that contain
varargs invocations where the supplied array is a subtype of the
declared varargs array type.

Closes gh-32804
2024-05-14 14:13:46 +02:00
Sam Brannen 8fe4493a7d Revise compilation support in SpEL for varargs array subtypes
This commit first reverts changes to SpelNodeImpl from the previous
commit in order to reduce the scope of the overall change set.

This commit then implements a different approach to support type-safe
checks for array subtype compatibility.

In order to support backward compatibility, this commit also
reintroduces generateCodeForArguments(MethodVisitor, CodeFlow, Member,
SpelNodeImpl[]) in deprecated form.

See gh-32804
2024-05-14 14:09:35 +02:00
Mikaël Francoeur 12727a2c4f Support compilation of varargs invocations in SpEL for array subtypes
This commit introduces support for compiling SpEL expressions that
contain varargs invocations where the supplied array is a subtype of
the declared varargs array type.

See gh-32804
2024-05-14 14:09:35 +02:00
Sam Brannen 29bb7b907c Polish SpelCompilationCoverageTests
See gh-32804

Co-authored-by: Mikaël Francoeur <mikael.francoeur@ticketmaster.com>
2024-05-14 14:09:34 +02:00
Juergen Hoeller 9516f87e5e Merge branch '6.1.x' 2024-05-14 13:48:01 +02:00
evan-mc 376d7839e0 Align StandaloneMockMvcBuilder with trailing slash defaults
Prior to this commit, trailing slash matching was disabled by default in
Spring MVC with gh-28552. `StandaloneMockMvcBuilder` was not changed as
a result and still had the trailing slash match option enabled.

This commit aligns the defaults in `StandaloneMockMvcBuilder` to better
reflect the expected behavior in tests.

Closes gh-32796
2024-05-14 09:15:56 +02:00
Brian Clozel ec4f3ea6d0 Merge branch '6.1.x'
Closes gh-32811
2024-05-13 21:26:56 +02:00
Brian Clozel cd56b47302 Merge branch '6.1.x' 2024-05-13 21:19:21 +02:00
mysend12 3547491590 Handle DataBufferLimitException as HTTP 413 responses
Prior to this commit, `DataBufferLimitException` would be thrown by
codecs when the request body was too large for the configured buffer
limit. This exception would not be handled by the web infrastructure and
would result in an HTTP 500 server error.

This commit introduces a new `PayloadTooLargeException` type that will
result in an HTTP 413 "Payload too large" response status.

Closes gh-32558
2024-05-13 14:07:21 +02:00
Brian Clozel 80ec951fcf Merge branch '6.1.x' 2024-05-13 13:15:01 +02:00
rstoyanchev 10e3d3b434 Merge branch '6.1.x' 2024-05-13 11:41:22 +01:00
rstoyanchev 2c9ed4608f Improve RequestAttributesThreadLocalAccessor
Ensure access to request attributes after initial REQUEST dispatch
is done, and the RequestAttributes markedCompleted.

Closes gh-32296
2024-05-13 11:41:07 +01:00
rstoyanchev 3ada9a0c79 Polishing in tests of ThreadLocalAccessor implementations
See gh-32296
2024-05-13 11:41:07 +01:00
Brian Clozel 5cb4985234 Merge branch '6.1.x' 2024-05-13 10:44:09 +02:00
Sam Brannen 19792f9670 Merge branch '6.1.x' 2024-05-12 12:32:20 +02:00
Sam Brannen f629fc9dc5 Polish Javadoc for SmartInstantiationAwareBeanPostProcessor 2024-05-11 16:13:26 +02:00
Sam Brannen 589b6dbba0 Merge branch '6.1.x' 2024-05-11 16:01:31 +02:00
Juergen Hoeller b3005fcdde Merge branch '6.1.x' 2024-05-10 13:53:04 +02:00
Juergen Hoeller b2d43f54e0 Merge branch '6.1.x' 2024-05-10 12:29:49 +02:00
earlgrey02 797f586f9a Polish RouterFunctionDsl
Place the lambda outside the method call.

Closes gh-32765
2024-05-10 12:15:22 +02:00
Sébastien Deleuze 8ac39d0062 Polishing 2024-05-10 11:57:42 +02:00
Sébastien Deleuze 7985ab33f4 Throw an exception for suspending factory methods
Suspending factory methods are not supported, and can
have side effects, so it is better to fail explicitly
for such use case.

Closes gh-32719
2024-05-10 11:56:50 +02:00
Yanming Zhou a02861f7db Insist on using CollectionUtils.isEmpty() and StringUtils.hasLength()
search `(\w+) != null && !(\1).isEmpty\(\)`
2024-05-09 09:25:09 +02:00
Yanming Zhou 8137cc9566 Polishing 2024-05-08 18:20:13 +02:00
Juergen Hoeller 7b16988ec9 Merge branch '6.1.x' 2024-05-08 17:52:42 +02:00
Stéphane Nicoll 8d90817ba3 Polish and fix build failure 2024-05-08 15:59:28 +02:00
Stéphane Nicoll 36295e664a Merge branch '6.1.x' 2024-05-08 15:47:10 +02:00
Sam Brannen 3ce79c3fbf Polishing 2024-05-08 11:54:55 +03:00
Chad Attermann cb48077698 Adds support for qualifying columns with table.
Adds support common in other ResultSet implemenatations for qualifying column names with table name to distinguish potentially duplicate column names in a join of two or more tables from one another. The expected format is {table_name}.{column_namne}, where column_name is the actuall designated column name and not the column label.
2024-05-08 10:35:55 +02:00
Stéphane Nicoll 2d942a6e91 Merge branch '6.1.x' 2024-05-08 10:30:19 +02:00
Stéphane Nicoll 168276aaab Review AssertJ support for MockMvc
Closes gh-32712
2024-05-07 16:30:44 +02:00
Stéphane Nicoll bcecce7aac Add shortcuts for frequently used assertions
See gh-32712
2024-05-07 16:28:44 +02:00
Stéphane Nicoll c8967de065 Provide better name for entry points
This commit renames AssertableMockMvc to MockMvcTester as the former
was too mouthful, and we are looking for a naming pattern that can be
applied consistently to test utilities that rely on AssertJ.

Rather than AssertableMvcResult, we now use MvcTestResult and it no
longer extends from MvcResult itself. That avoids having existing code
that is migrated to the new API whilst assigning the result to MvcResult
and get a bad DevXP as that doesn't bring any of the AssertJ support.

See gh-32712
2024-05-07 16:28:44 +02:00
Stéphane Nicoll 5567d14700 Move response body directly in AbstractMockHttpServletResponseAssert
This commit removes ResponseBodyAssert and rather offers first-class
access support for the response body at the root level using bodyText(),
bodyJson(), and body().

This avoids a double navigation to assert the response body.

See gh-32712
2024-05-07 16:28:44 +02:00
Stéphane Nicoll 24cc77655f Merge JSON assertions in a single Assert type
This commit merges the JSONCompare and JsonPath support in a single
assert object. The rationale for the previous situation was that
JsonPath is optional but merging the assertions methods do not make
it mandatory as the usage if JsonPath is triggered only if it is
actually used.

See gh-32712
2024-05-07 16:28:44 +02:00
Juergen Hoeller 645556a28c Merge branch '6.1.x' 2024-05-07 15:53:00 +02:00
Sam Brannen ac0136b75c Clarify supported targets for annotations in the TestContext framework
Closes gh-32772
2024-05-07 16:45:12 +03:00
Juergen Hoeller 3048cfe9d9 Polishing 2024-05-06 20:16:07 +02:00
Juergen Hoeller d9ca263065 Merge branch '6.1.x' 2024-05-06 20:11:56 +02:00
Sébastien Deleuze 39889744b0 Polish JdbcTemplate Best Practices section 2024-05-06 18:28:54 +02:00
Sébastien Deleuze 12272d6e41 Remove unneeded @SuppressWarnings("NullAway")
Closes gh-32542
2024-05-06 18:28:54 +02:00
Stéphane Nicoll 93dcbc41b0 Polish 2024-05-06 17:40:23 +02:00
Stéphane Nicoll 371d1a02f2 Fix typo 2024-05-06 17:38:52 +02:00
Sébastien Deleuze 04944a1f56 Modernize the lazy-initialized beans refdoc section
Closes gh-32767
2024-05-06 17:08:49 +02:00
Sébastien Deleuze c6459b40e4 Modernize the integration section of the refdoc
This commit adds Java and Kotlin tabs to XML code snippets where
relevant, and leverages code includes.

Closes gh-32600
2024-05-06 16:05:35 +02:00
Sam Brannen bdc4ecd599 Merge branch '6.1.x'
# Conflicts:
#	spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java
2024-05-03 17:12:40 +03:00
Arjen Poutsma 92f54da628 Bug fixes in WebMvc.fn testing support
See gh-30477
2024-05-03 14:28:21 +02:00
Arjen Poutsma 8bca7cd8e7 Various UrlParser improvements
- Consistent use of codePointAt instead of charAt.
- Fix bug in domainToAscii

See gh-32513
2024-05-03 13:41:56 +02:00
Arjen Poutsma 4df80a8d09 Various CPU and memory improvements in UrlParser
Improvements include:
- Replace throwing exceptions with failure results in hot areas,
- Verify digits of a string before passing it to Integer::parseInt
- Lazily initialization of fields
- Using LinkedList instead of ArrayList where size is not known
  beforehand

See gh-32513
2024-05-03 13:41:55 +02:00
Sam Brannen 12b285add3 Polishing 2024-05-03 14:03:50 +03:00
Sam Brannen b0a2f269cb Merge branch '6.1.x' 2024-05-03 12:18:34 +03:00
Sébastien Deleuze 8de7c64637 Upgrade to webjars-locator-lite 1.0.0
Closes gh-32564
2024-05-03 09:38:31 +02:00
Stéphane Nicoll 305ebca56d Merge branch '6.1.x' 2024-05-02 17:02:08 +02:00
Brian Clozel 09ca4cdc70 Merge branch '6.1.x' 2024-05-02 16:01:56 +02:00
Arjen Poutsma 7ccd4ce886 Reduce logging level in UrlParser
See gh-32513
2024-05-02 15:33:06 +02:00
Arjen Poutsma 8f3b7484e2 Add testing support for WebMvc.fn
This commit introduces testing support for WebMvc.fn in the form of a
RouterFunctionMockMvcBuilder and RouterFunctionMockMvcSpec.

Closes gh-30477
2024-05-02 15:20:56 +02:00
Stéphane Nicoll d708343a05 Upgrade to HtmlUnit 4.1.0
As well as Selenium 4.20.0

Closes gh-32740
2024-05-02 13:45:31 +02:00
Sam Brannen 1ffd0dcf06 Merge branch '6.1.x' 2024-05-02 13:29:08 +03:00
Stéphane Nicoll 759ec52fd2 Merge branch '6.1.x' 2024-05-02 11:49:24 +02:00
Sébastien Deleuze a8430878ca Merge branch '6.1.x' 2024-05-02 11:08:30 +02:00
Arjen Poutsma 3897ea78bb Resolve collisions in composite collections
Before this commit, creating a CompositeMap from two maps with the same
key has strange results, such as entrySet returning duplicate entries
with the same key.

After this commit, we give precedence to the first map by filtering out
all entries in the second map that are also mapped by the first map.

See gh-32245
2024-05-02 10:51:57 +02:00
Arjen Poutsma d5664ba01a Ensure old attributes are not removed by accident
This commit ensures that a copy is made of old attributes before
replacing it with new attributes. Because new attributes can be
composed of old, clearing the old would also remove entries from the
 new.

See gh-32245
2024-05-02 10:51:38 +02:00
George Papadopoulos 3f3995f846 Add ServerRequest.pathVariableOrNull Kotlin extension
Closes gh-32738
2024-05-02 10:11:37 +02:00
Brian Clozel 02b3801f1a Merge branch '6.1.x' 2024-05-02 09:17:01 +02:00
Juergen Hoeller 69eaf8f828 Merge branch '6.1.x' 2024-05-01 18:07:13 +02:00
Juergen Hoeller 649a537891 Merge branch '6.1.x' 2024-05-01 15:42:14 +02:00
Sam Brannen 153d1bc923 Support Collection target types in custom IndexAccessors
Prior to this commit, an IndexAccessor could not provide support for a
Collection target type, since the built-in support for indexing into a
Collection in SpEL's Indexer took precedence.

This commit allows an IndexAccessor to support custom Collection target
types by separating the built-in List and Collection support and
applying the built-in Collection support after custom index accessors
have been applied.

Closes gh-32736
2024-05-01 16:12:04 +03:00
Sam Brannen 4e6591e1a9 Polishing 2024-05-01 16:12:04 +03:00
Arjen Poutsma 37dd47c6fc Set request timeout in JettyClientHttpRequestFactory
Closes gh-32330
2024-05-01 09:58:35 +02:00
Brian Clozel 57dd2f46db Merge branch '6.1.x' 2024-04-30 19:11:01 +02:00
Sam Brannen 512ff7ce3c Merge branch '6.1.x' 2024-04-30 18:09:41 +03:00
Arjen Poutsma a3afe51c9f Fix guard against multiple subscriptions
This commit changes the guard against multiple subscriptions, as the
previously used doOnSubscribe hook could not function as guard in
certain scenarios.

Closes gh-32727
2024-04-30 16:06:53 +02:00
Juergen Hoeller f1a1190700 Merge branch '6.1.x'
# Conflicts:
#	framework-docs/modules/ROOT/pages/data-access/transaction/declarative/annotations.adoc
2024-04-30 15:47:48 +02:00
Sam Brannen 35c183d634 Introduce ReflectiveIndexAccessor in SpEL
This commit introduces ReflectiveIndexAccessor for the Spring
Expression Language (SpEL) which is somewhat analogous to the
ReflectivePropertyAccessor implementation of PropertyAccessor.

ReflectiveIndexAccessor is a flexible IndexAccessor implementation that
uses reflection to read from and optionally write to an indexed
structure of a target object. ReflectiveIndexAccessor also implements
CompilableIndexAccessor in order to support compilation to bytecode for
read access.

For example, the following creates a read-write IndexAccessor for a
FruitMap type that is indexed by Color, including built-in support for
compilation to bytecode for read access.

IndexAccessor indexAccessor = new ReflectiveIndexAccessor(
    FruitMap.class, Color.class, "getFruit", "setFruit");

Closes gh-32714
2024-04-30 16:26:11 +03:00
Sébastien Deleuze b232aefdeb Use lowercase package in code snippets
Closes gh-32734
2024-04-30 15:05:15 +02:00
Stéphane Nicoll 2b62897dc5 Merge pull request #32064 from awmeinema
* pr/32064:
  Polish "Allow AbstractUrlHandlerMapping to add/remote handlers"
  Allow AbstractUrlHandlerMapping to add/remote handlers

Closes gh-32064
2024-04-30 13:48:54 +02:00
Stéphane Nicoll ad0c488767 Polish "Allow AbstractUrlHandlerMapping to add/remote handlers"
See gh-32064
2024-04-30 13:48:34 +02:00
ameinema 109d985f89 Allow AbstractUrlHandlerMapping to add/remote handlers
See gh-32064
2024-04-30 12:58:29 +02:00
Stéphane Nicoll a7ceedf2bb Merge pull request #28958 from jerzykrlk
* pr/28958:
  Polish "Include URL and HTTP method in DefaultResponseErrorHandler"
  Include URL and HTTP method in DefaultResponseErrorHandler

Closes gh-28958
2024-04-30 11:37:24 +02:00
Stéphane Nicoll f85c4e1ea7 Polish "Include URL and HTTP method in DefaultResponseErrorHandler"
See gh-28958
2024-04-30 11:34:17 +02:00
jerzykrlk 4972d18dd6 Include URL and HTTP method in DefaultResponseErrorHandler
See gh-28958
2024-04-30 11:34:17 +02:00
Sébastien Deleuze 2350c8a8f5 Remove Hibernate substitutions and deprecated properties usage
This commit raises the baseline to Hibernate 6.5 for native support,
which allows to remove remaining Hibernate substitutions as well as
deprecated properties usage.

Closes gh-32314
2024-04-30 11:24:19 +02:00
Juergen Hoeller dfc053ad65 Merge branch '6.1.x'
# Conflicts:
#	framework-platform/framework-platform.gradle
2024-04-29 18:05:40 +02:00
Stéphane Nicoll 87855e2d34 Clear ShadowMatch instances when they are no longer needed
This commit gathers the ShadowMatch instances that AspectJ requires in
a dedicated class that can be used to clear the instances when they are
no longer required.

As those are mainly triggered via AspectJAwareAdvisorAutoProxyCreator,
it now implements the necessary callbacks to clear the cache.

Closes gh-12334
2024-04-29 16:25:11 +02:00
Sébastien Deleuze 331bdb066e Add BodyInserters.fromValue(T, ParameterizedTypeReference<T>)
This commit introduces
BodyInserters.fromValue(T, ParameterizedTypeReference<T>) variant as
well as related WebClient.RequestBodySpec API,
ServerResponse.BodyBuilder API and Kotlin extensions.

Closes gh-32713
2024-04-29 15:28:42 +02:00
Stéphane Nicoll 9492d88270 Stop wrapping low-level exceptions in CacheAspectSupport initialization
This commit replaces the IllegalStateException thrown in
CacheAspectSupport when a CacheManager cannot be determined. These were
added to provide a dedicated error message, but it is possible to do
so without hiding the more adequate exception type.

Closes gh-22442
2024-04-29 15:24:36 +02:00
Sam Brannen 9b85c93b6b Polishing 2024-04-29 16:17:38 +03:00
Brian Clozel 65f118bdec Merge branch '6.1.x' 2024-04-29 13:40:51 +02:00
Sam Brannen fc3dddac9c Polishing 2024-04-28 11:43:14 +03:00
donghui a662f5e719 Add a requiredBody() extension to RestClient.ResponseSpec
Closes gh-32703
2024-04-26 16:21:32 +02:00
Sam Brannen 3002813598 Clean up warnings in spring-test 2024-04-26 15:49:09 +03:00
Arjen Poutsma 3cfa4ed4f7 Various UrlParser improvements
See gh-32513
2024-04-26 11:44:32 +02:00
Sam Brannen 27d2200058 Introduce CompilableIndexAccessor SPI in SpEL
This commit introduces a new CompilableIndexAccessor SPI for the Spring
Expression Language (SpEL) which allows an IndexAccessor to support
compilation to bytecode for operations that read an index.

This analogous to the CompilablePropertyAccessor SPI.

This commit also includes a prototype for a general purpose
ReflectiveIndexAccessor in the tests.

Closes gh-32613
2024-04-26 12:19:10 +03:00
Sam Brannen 0b5800ae39 Introduce generateCodeForArgument() in CodeFlow
Closes gh-32708
2024-04-25 15:12:21 +03:00
Sam Brannen 25fd5659cb Polishing 2024-04-25 15:10:00 +03:00
Sam Brannen de9ce800bf Polishing 2024-04-25 14:00:35 +03:00
Sam Brannen 8f579b3144 Make SpelNode compilation aware
In order to make SpelNode compilation aware, this method moves the
declaration of isCompilable() and generateCode(...) from SpelNodeImpl
to SpelNode.

Closes gh-32707
2024-04-25 13:47:57 +03:00
Sam Brannen 14689256c4 Support String index type in custom IndexAccessor
Closes gh-32706
2024-04-25 12:17:13 +03:00
Sam Brannen a3d3bc0a1f Polishing 2024-04-25 12:14:23 +03:00
Sam Brannen a986374da7 Use records to track cached state in Indexer 2024-04-25 11:51:11 +03:00
Simon Baslé 4e6325a277 Merge branch '6.1.x' 2024-04-25 10:09:23 +02:00
Sam Brannen aaf33100d9 Add IndexAccessor support to SpEL's SimpleEvaluationContext
Closes gh-32702
2024-04-24 16:19:39 +03:00
Sam Brannen 1e4275a0f9 Implement optional get<list>() methods in EvaluationContext as default methods 2024-04-24 15:54:05 +03:00
Sam Brannen 33fbd7141d Make AstUtils package-private
AstUtils was never intended to be a public utility class.
2024-04-24 15:44:02 +03:00
Sam Brannen 461d7a82f6 Avoid redefining getSpecificTargetClasses() in [Property|Index]Accessor 2024-04-24 15:39:01 +03:00
Sam Brannen 00da781c44 Merge branch '6.1.x' 2024-04-24 15:33:19 +03:00
Juergen Hoeller 0402ea13c0 Merge branch '6.1.x' 2024-04-24 13:42:43 +02:00
Stéphane Nicoll 6682d75481 Merge branch '6.1.x' 2024-04-24 11:13:27 +02:00
rstoyanchev 75a5409c97 Add PreFlightRequestHandler for Spring MVC
This is equivalent of the same contract for WebFlux. It is implemented
by HandlerMappingIntrospector, and may be called directly by Spring
Security to handle a pre-flight request without delegate to the rest
of the filter chain.

HandlerMappingIntrospector also has the boolean method
allHandlerMappingsUsePathPatternParser that checks whether all handler
mappings are configured to use parsed PathPattern's.

See gh-31823
2024-04-23 19:00:10 +01:00
Juergen Hoeller 3991cae875 Merge branch '6.1.x' 2024-04-23 16:07:46 +02:00
Stéphane Nicoll a77895bd90 Merge branch '6.1.x' 2024-04-23 15:34:49 +02:00
Stéphane Nicoll ed188f82d8 Merge branch '6.1.x' 2024-04-23 15:25:20 +02:00
Arjen Poutsma df41bb1ae9 Merge pull request #32622 from NadChel:bugfix/GH-32620_remove_content_type_if_no_body
* gh-32622:
  Polishing external contribution
  Remove Content-Type when body is empty
2024-04-23 15:04:53 +02:00
Arjen Poutsma 6c5ef9715b Polishing external contribution
See gh-32622
2024-04-23 14:59:28 +02:00
Sergey Zolotaryov 3f8a10c19f Remove Content-Type when body is empty
Closes gh-32622
See gh-32620
2024-04-23 14:58:20 +02:00
Juergen Hoeller adc7f73170 Merge branch '6.1.x' 2024-04-23 13:17:08 +02:00
Stéphane Nicoll af4a986256 Merge branch '6.1.x' 2024-04-23 12:43:40 +02:00
Stéphane Nicoll 4e1b8f9be3 Merge branch '6.1.x' 2024-04-23 11:04:02 +02:00
Sam Brannen ca6d987c56 Support compilation of array and list indexing with Integer in SpEL
Prior to this commit, the Spring Expression Language (SpEL) failed to
compile an expression that indexed into any array or list using an
Integer.

This commit adds support for compilation of such expressions by
ensuring that an Integer is unboxed into an int in the compiled
bytecode.

Closes gh-32694
2024-04-23 11:36:48 +03:00
Stéphane Nicoll 8124491249 Merge branch '6.1.x' 2024-04-22 17:01:04 +02:00
Stéphane Nicoll c21090ad31 Merge branch '6.1.x' 2024-04-22 15:16:50 +02:00
Stéphane Nicoll 9f46dd7d86 Merge branch '6.1.x' 2024-04-22 14:52:31 +02:00
Juergen Hoeller 6a1ec0ed73 Merge branch '6.1.x' 2024-04-22 13:43:57 +02:00
Stéphane Nicoll b965336467 Merge branch '6.1.x' 2024-04-22 12:13:14 +02:00
Brian Clozel c80bf57834 Merge branch '6.1.x' 2024-04-20 19:46:23 +02:00
Sam Brannen 80fb8ea813 Avoid unnecessary compilation attempts in SpEL's Indexer
Closes gh-32677
2024-04-19 17:20:24 +02:00
Sam Brannen 1eed71bb1d Polishing 2024-04-19 14:34:09 +02:00
Sam Brannen c8090fe0b4 Extract generateIndexCode() in SpEL's Indexer 2024-04-19 14:33:22 +02:00
Sam Brannen 9eab7bb11d Introduce null-safe indexing test for custom IndexAccessor 2024-04-19 14:29:43 +02:00
Sam Brannen a01f7cefae Polish Javadoc for SpelNode 2024-04-19 14:27:11 +02:00
Brian Clozel ccf9ba86a0 Merge branch '6.1.x' 2024-04-18 21:45:23 +02:00
Brian Clozel 0893cf948d Merge branch '6.1.x' 2024-04-18 21:27:50 +02:00
Arjen Poutsma 62a1b5558f Resolve UrlParser review comments
See gh-32513
2024-04-18 15:20:02 +02:00
rstoyanchev aa05a6a3b3 Merge branch '6.1.x' 2024-04-18 14:18:43 +01:00
Simon Baslé a985c0bb35 Polishing: fix link fragments in test bean override annotation pages 2024-04-18 14:54:57 +02:00
Juergen Hoeller e42c5ca52b Merge branch '6.1.x' 2024-04-18 12:20:14 +02:00
Sam Brannen f96d0a6c4a Merge branch '6.1.x' 2024-04-18 11:54:02 +02:00
rstoyanchev 0bc447c304 Jackson decoder supports array/Collection elements
Closes gh-32579
2024-04-18 10:40:25 +01:00
Michael Kunze 00c7002354 Fix typo in class name
Closes gh-32664
2024-04-18 09:32:10 +02:00
Onji Kim ec055da7c3 Reject negative Content-Length values in HttpHeaders
Prior to this commit, `HttpHeaders#setContentLength` would accept
negative values. Those are not allowed by the RFC and the headers
implementation only uses "-1" as a way to convey that no value was set.

This commit ensures that negative values are rejected.

Fixes gh-32660
2024-04-17 23:05:13 +02:00
Brian Clozel c03f798dad Merge branch '6.1.x' 2024-04-17 22:47:44 +02:00
Arjen Poutsma af23cc233e Add support for state override in new URL parser
See gh-32513
2024-04-17 16:33:53 +02:00
Arjen Poutsma f21e05a9e4 Introduce new URL parser
This commit introduces a new URL parser based on algorithm provided in
the Living URL standard. This new UrlParser is used by
UriComponentsBuilder::fromUriString, replacing the regular expressions.

Closes gh-32513
2024-04-17 11:17:01 +02:00
Sam Brannen 8727d723f3 Polish "Bean Overriding in Tests" support 2024-04-16 17:08:15 +02:00
Simon Baslé ce8758b83c Merge branch '6.1.x' 2024-04-16 16:47:01 +02:00
Simon Baslé 43b5c81dad Merge branch '6.1.x' 2024-04-16 16:29:53 +02:00
Sam Brannen 69cb2a347a Update copyright date
See gh-32647
2024-04-16 15:11:12 +02:00
janghs 0d4ef4eafe Omit empty resource description in DeprecatedBeanWarner's log message
Closes gh-32647
2024-04-16 15:07:18 +02:00
Arjen Poutsma 051fd2ae4f Merge branch '6.1.x' 2024-04-16 11:44:02 +02:00
Simon Baslé 02ee5de470 Document bean override in TestContext framework section of the manual
This change splits the documentation in the reference manual: the
`@TestBean`, `@MockitoBean` and `@MockitoSpyBean` annotations are kept
in the appendix and the general documentation about the feature is moved
into a dedicated sub-section of the TCF section.

Close gh-32490
2024-04-15 17:07:14 +02:00
Sam Brannen afbce96fb7 Introduce getName() in SpEL's BeanReference
Closes gh-32640
2024-04-15 16:44:37 +02:00
Sam Brannen 3f34a13436 Polishing 2024-04-15 16:30:51 +02:00
Simon Baslé fe83003324 Merge branch '6.1.x' 2024-04-15 14:21:51 +02:00
laterlaugh 5012843b12 Fix typos in comment and test
Closes gh-32628
2024-04-12 17:40:54 +02:00
Sam Brannen b13963ee81 Reject private JUnit Jupiter lifecycle methods annotated with @⁠Autowired
Closes gh-32627
2024-04-12 14:27:38 +02:00
Sam Brannen 43cc18a5a1 Use literal monospace in SpEL templating section
Prior to this commit, the text `#{ }` was displayed as { } with a bright
green background.

This commit addresses this by making use of Asciidoc's literal monospace
feature: `+#{ }+`.
2024-04-11 14:00:46 +02:00
Brian Clozel 4d5536db21 Merge branch '6.1.x' 2024-04-11 11:33:16 +02:00
Brian Clozel d99113661e Merge branch '6.1.x' 2024-04-11 11:24:58 +02:00
Brian Clozel 54dab45151 Merge branch '6.1.x' 2024-04-11 08:51:13 +02:00
Stéphane Nicoll 2e3a923225 Polish 2024-04-11 08:45:26 +02:00
Juergen Hoeller 77c6f160fe Merge branch '6.1.x'
# Conflicts:
#	spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java
#	spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java
#	spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/RequestParamMethodArgumentResolverTests.java
2024-04-10 18:29:48 +02:00
Simon Baslé 2d33aac350 Improve Bean Overriding support, testing and documentation
This commit improves on the bean overriding feature in several ways:
the API is simplified and polished (metadata and processor contracts,
 etc...).

The commit also reworks infrastructure classes (context customizer,
test execution listener, BeanOverrideBeanFactoryPostProcessor, etc...).
Parsing of annotations is now fully stateless.

In order to avoid OverrideMetadata in bean definition and to make a
first step towards AOT support, the BeanOverrideBeanFactoryPostProcessor
now delegates to a BeanOverrideRegistrar to track classes to parse,
the metadata-related state as well as for the field injection methods
for tests.

Lastly, this commit increases the test coverage for the provided
annotations and adds integration tests and fixes a few `@TestBean`
issues.
2024-04-10 18:16:43 +02:00
Sébastien Deleuze 711ddd1ac6 Merge branch '6.1.x' 2024-04-10 17:55:09 +02:00
Brian Clozel 35a0d24dc3 Merge branch '6.1.x' 2024-04-10 16:44:20 +02:00
Sam Brannen ae3dc0d4b1 Merge SpEL IndexAccessor feature into main
This set of commits introduces a new IndexAccessor SPI for the Spring
Expression Language (SpEL) which allows third parties to customize the
SpEL Indexer.

A custom IndexAccessor implementation can be registered in a
StandardEvaluationContext.

For an example, see the JacksonArrayNodeIndexAccessor in IndexingTests
in the spring-expression module.

See gh-26409
Closes gh-26478
2024-04-10 16:07:33 +02:00
Sam Brannen 1c9cff668c Honor IndexAccessor#canWrite() and cache accessors
See gh-26409
See gh-26478
2024-04-10 15:54:34 +02:00
Sam Brannen 15511890bd Revise Javadoc for IndexAccessor
See gh-26409
See gh-26478
2024-04-10 15:54:34 +02:00
Sam Brannen 22bfe7da5a Introduce proper error handling for IndexAccessor support in SpEL
See gh-26409
See gh-26478
2024-04-10 15:54:34 +02:00
Sam Brannen 5c6b82a947 Change IndexAccessor#read()'s return type to TypedValue
Prior to this commit, the read() method in the IndexAccessor SPI
declared a return type of ValueRef which introduced a package cycle.

This commit addresses this by aligning with the PropertyAccess SPI and
returning TypedValue from IndexAccessor's read() method. This commit
also reworks the internals of Indexer based on a new, local
IndexAccessorValueRef implementation.

See gh-26409
See gh-26478
2024-04-10 15:54:34 +02:00
Sam Brannen b7c3833732 Revise null-safety contracts in IndexAccessor SPI
When indexing into an object, the target object can never be null.

See gh-26409
See gh-26478
2024-04-10 15:54:34 +02:00
Sam Brannen d91277095a Initial review and polish of IndexAccessor support in SpEL
See gh-26409
See gh-26478
2024-04-10 15:54:34 +02:00
ljmn3211 50a0000ed7 Introduce IndexAccessor SPI to customize the SpEL Indexer
See gh-26409
See gh-26478
2024-04-10 15:14:50 +02:00
Sébastien Deleuze d6e9562aeb Revert "Prevent compilation warnings with @Nullable"
This reverts commit dc242df533.
2024-04-10 14:39:47 +02:00
Juergen Hoeller f6089afd0e Use ClassLoaderAwareGeneratorStrategy with UndeclaredThrowableStrategy delegate
See gh-32469
2024-04-10 13:23:24 +02:00
Mikaël Francoeur 561583842f Consistent handling of undeclared checked exceptions in CGLIB proxies (#32469)
Co-authored-by: hengyunabc <hengyunabc@gmail.com>
Co-authored-by: Mikaël Francoeur <mikael.francoeur@ticketmaster.com>
2024-04-10 12:11:33 +02:00
Sam Brannen bed3689001 Polishing 2024-04-10 10:56:41 +02:00
Stéphane Nicoll 3d85ec2354 Merge pull request #32610 from alidandach
* pr/32610:
  Polish "Use try-with-resources when applicable"
  Use try-with-resources when applicable

Closes gh-32610
2024-04-10 09:52:25 +02:00
Stéphane Nicoll 330dcf15bc Polish "Use try-with-resources when applicable"
See gh-32610
2024-04-10 09:49:35 +02:00
ali dandach 82b51479d5 Use try-with-resources when applicable
See gh-32610
2024-04-10 09:49:17 +02:00
Stéphane Nicoll 2d408685ce Merge branch '6.1.x' 2024-04-09 19:53:43 +02:00
Stéphane Nicoll cc7815145f Upgrade to Reactor 2024.0.0-M1
Closes gh-32591
2024-04-09 19:51:42 +02:00
rstoyanchev a1586ff0a0 Merge branch '6.1.x' 2024-04-09 18:25:11 +01:00
Sam Brannen 0637b699cd Merge branch '6.1.x' 2024-04-09 19:01:38 +02:00
Brian Clozel c3aa0cd815 Merge branch '6.1.x' 2024-04-09 18:36:53 +02:00
Juergen Hoeller a9fffa844f Merge branch '6.1.x' 2024-04-09 16:15:19 +02:00
Sébastien Deleuze bb04c6b1f3 Merge branch '6.1.x' 2024-04-09 15:22:58 +02:00
Stéphane Nicoll cb8b7a455a Start building against Reactor 2024.0.0-M1 snapshots
See gh-32591
2024-04-09 13:54:10 +02:00
Stéphane Nicoll 9dbb269bb9 Merge branch '6.1.x' 2024-04-09 13:40:03 +02:00
Stéphane Nicoll f3aa61c5bb Upgrade to Micrometer 1.13.0-RC1
Closes gh-32595
2024-04-09 13:39:49 +02:00
Sébastien Deleuze dc242df533 Prevent compilation warnings with @Nullable
This commit replaces `@Nonnull(when = When.MAYBE)` meta-annotation in
`org.springframework.lang.Nullable` by `@CheckForNull` in order to
prevent `unknown enum constant When.MAYBE` compilation warnings.

Closes gh-27183
2024-04-09 13:35:49 +02:00
Sébastien Deleuze c05b4bcd71 Add missing dependency and remove warnings
See gh-22171
2024-04-09 12:50:36 +02:00
Sébastien Deleuze 246f291f67 Use code includes and tabs in WebSocket documentation
See gh-22171
2024-04-09 12:21:36 +02:00
Juergen Hoeller 4eb93da31d Merge branch '6.1.x'
# Conflicts:
#	spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java
2024-04-08 22:41:02 +02:00
Sébastien Deleuze cabd1f53df Polishing 2024-04-08 18:03:19 +02:00
Sébastien Deleuze a471aa6a9a Use code includes and tabs in Regexp Pointcuts documentation
See gh-22171
2024-04-08 18:03:10 +02:00
Sébastien Deleuze 515295e205 Use code includes and tabs in AOP documentation
See gh-22171
2024-04-08 16:54:45 +02:00
Sam Brannen 1455a9fe22 Polishing 2024-04-08 16:37:47 +02:00
Stéphane Nicoll 36636b000b Consistent newline at the end of files
See gh-22171
2024-04-08 13:34:44 +02:00
Sébastien Deleuze 94050b3638 Use code includes and tabs in SpEL documentation
See gh-22171
2024-04-08 11:53:56 +02:00
Stéphane Nicoll 62db2680f5 Merge branch '6.1.x' 2024-04-08 11:11:15 +02:00
Sébastien Deleuze 5c8043dcea Use code includes and tabs in date/time format documentation
See gh-22171
2024-04-08 10:32:16 +02:00
Juergen Hoeller 13df59cef4 Merge branch '6.1.x' 2024-04-08 09:52:42 +02:00
Sébastien Deleuze 78f08ed1dd Upgrade to webjars-locator-lite 0.0.4
See gh-27619
2024-04-08 09:33:35 +02:00
Juergen Hoeller 03dd8e20e2 Merge branch '6.1.x' 2024-04-05 18:05:37 +02:00
Arjen Poutsma dcab1f2237 Merge branch '6.1.x' 2024-04-05 16:13:58 +02:00
Stéphane Nicoll 3e93ca02cc Merge branch '6.1.x' 2024-04-05 15:58:08 +02:00
Stéphane Nicoll 583dc04986 Merge branch '6.1.x' 2024-04-05 15:57:39 +02:00
Stéphane Nicoll 749d7671a0 Stop linking to Websphere's Javadoc
Closes gh-32581
2024-04-05 15:54:41 +02:00
Arjen Poutsma 08bc959c9b Merge branch '6.1.x' 2024-04-05 15:43:49 +02:00
Sébastien Deleuze 4e6532f639 Merge branch '6.1.x' 2024-04-05 15:00:48 +02:00
Sébastien Deleuze 0c42874629 Merge branch '6.1.x' 2024-04-05 14:33:20 +02:00
Stéphane Nicoll 5253af1a04 Merge branch '6.1.x' 2024-04-04 14:43:38 +02:00
Stéphane Nicoll e3281a527c Merge branch '6.1.x' 2024-04-02 18:01:59 +02:00
Stéphane Nicoll a96f22d5e2 Merge branch '6.1.x' 2024-04-02 15:48:04 +02:00
Stéphane Nicoll 7a74e45946 Make use of bean definition overriding more visible
This commit makes the use of bean definition overriding more visible and
prepare for a deprecation of the feature in the next major release.

As of this commit, use of bean definition overriding logs at INFO level.
The previous log level can be restored by setting the
allowBeanDefinitionOverriding flag explicitly on the BeanFactory (or
via the related ApplicationContext).

A number of tests that are using bean overriding on purpose have been
updated to set this flag, which will make them easier to find once we
actually deprecate the feature.

Closes gh-31288
2024-04-02 14:05:12 +02:00
Sébastien Deleuze 8af3eb1aea Polishing
See gh-22171
2024-04-02 13:33:29 +02:00
Sébastien Deleuze d26265420a Avoid removal warnings in tests
See gh-27619
2024-04-02 13:19:34 +02:00
Sébastien Deleuze bede619e7d Refine @EnableWebFlux documentation
This commit refines the documentation related to `@EnableWebFlux` in
order to make it more relevant for modern Boot applications.

See gh-22171
2024-04-02 11:48:12 +02:00
Sébastien Deleuze bf2a72b043 Upgrade webjars-locator-lite to 0.0.3
See gh-27619
2024-04-02 10:22:31 +02:00
Sébastien Deleuze 67edcde0a2 Introduce support for webjars-locator-lite
This commit introduces support for org.webjars:webjars-locator-lite
via a new LiteWebJarsResourceResolver in Spring MVC and WebFlux, and
deprecates WebJarsResourceResolver which is performing a classpath
scanning that slows down application startup.

Closes gh-27619
2024-04-02 10:16:51 +02:00
Stéphane Nicoll 81bc586e15 Merge branch '6.1.x' 2024-04-02 09:34:39 +02:00
Stéphane Nicoll 6f95af978e Merge branch '6.1.x' 2024-04-01 12:14:56 +02:00
Sam Brannen d360def92e Polishing 2024-03-30 16:06:06 +01:00
Łukasz Jernaś 86b87d7d85 Prevent package summaries from being truncated due to incorrect sentence detection
Javadoc doesn't seem to like having `e.g.` as it thinks the sentence
ends there, which is usually incorrect and results in broken descriptions.

This commit rewords the doc strings slightly to avoid problematic parts.

Closes gh-32532
2024-03-30 15:44:41 +01:00
Sébastien Deleuze 186e70cd04 Use code includes and tabs in MVC Config documentation
Additional fixes.

See gh-22171
2024-03-29 18:24:50 +01:00
Sébastien Deleuze 91bb7d8daf Use code includes and tabs in MVC Config documentation
This commit also refines the documentation related to
`@EnableWebMvc` in order to make it more relevant for modern Boot
applications.

See gh-22171
2024-03-29 17:58:01 +01:00
Sébastien Deleuze db1010f9c9 Add CBOR support to AllEncompassingFormHttpMessageConverter
Closes gh-32428
2024-03-28 14:59:22 +01:00
Sébastien Deleuze cf87441a26 Remove unnecessary method.isAccessible() invocation
Closes gh-32548
2024-03-28 11:50:29 +01:00
Sébastien Deleuze dc982d0d51 Merge branch '6.1.x' 2024-03-28 11:14:42 +01:00
Sébastien Deleuze 996e66abdb Perform NullAway build-time checks in spring-test
Closes gh-32475
2024-03-26 18:14:56 +01:00
Sébastien Deleuze 96d9081190 Merge branch '6.1.x' 2024-03-26 18:10:25 +01:00
Stéphane Nicoll bf5b22d64f Merge branch '6.1.x' 2024-03-26 17:45:25 +01:00
Sébastien Deleuze 8b51b36729 Perform NullAway build-time checks in more modules
This commit enables null-safety build-time checks in
all remaining modules except spring-test.

See gh-32475
2024-03-26 15:53:01 +01:00
Sébastien Deleuze 2fea3d7921 Merge branch '6.1.x' 2024-03-26 15:41:46 +01:00
Sam Brannen f47352ff04 Polish PropertyOrFieldReference 2024-03-26 13:21:46 +01:00
Stéphane Nicoll c1ed504ac1 Avoid classpath scanning in test
This commit updates SpringConfiguratorTests to not rely on classpath
scanning as it could have side effect. In this particular case, the
configuration class that sources the scan is detected again, leading
to bean overriding.

Irrespective of that, adding more code in that package may have side
effect as they could be scanned as well.

Closes gh-32535
2024-03-26 10:41:00 +01:00
Sébastien Deleuze 5b660da52d Perform NullAway build-time checks in more modules
This commit enables null-safety build-time checks in:
 - spring-jdbc
 - spring-r2dbc
 - spring-orm
 - spring-beans
 - spring-aop

See gh-32475
2024-03-26 09:59:30 +01:00
Sébastien Deleuze 3b4f8dbb8e Merge branch '6.1.x' 2024-03-26 09:47:09 +01:00
Sam Brannen 4b0a048570 Polish SpEL internals and remove duplicate code 2024-03-25 17:47:14 +01:00
Sam Brannen 57632f9f08 Fix wording in SpEL's PropertyAccessor Javadoc
The documentation now properly refers to "property accessors" instead
of "resolvers".
2024-03-25 14:26:40 +01:00
Sébastien Deleuze 7c009ccc1f Perform NullAway build-time checks in spring-messaging
See gh-32475
2024-03-25 12:30:03 +01:00
Sébastien Deleuze f421f38e24 Merge branch '6.1.x' 2024-03-25 12:28:53 +01:00
Sébastien Deleuze 88e26a9570 Merge branch '6.1.x' 2024-03-25 11:28:43 +01:00
Sébastien Deleuze 2fc78dfb69 Perform NullAway build-time checks in spring-jms
See gh-32475
2024-03-25 11:27:04 +01:00
Sébastien Deleuze e52ee01ec8 Perform NullAway build-time checks in spring-web
Also in spring-websocket.

See gh-32475
2024-03-25 11:11:09 +01:00
Sébastien Deleuze 2b1eb488fc Merge branch '6.1.x' 2024-03-25 11:04:49 +01:00
Sam Brannen 5bec072dcb Polish SpEL internals 2024-03-24 17:07:42 +01:00
Stéphane Nicoll 8736ca038a Merge branch '6.1.x' 2024-03-24 09:23:10 +01:00
Stéphane Nicoll aa9f0b3cb2 Merge pull request #32524 from izeye
* pr/32524:
  Fix Javadoc since to ChannelRegistration.executor()

Closes gh-32524
2024-03-24 08:29:45 +01:00
Johnny Lim 56b14dc54a Fix Javadoc since to ChannelRegistration.executor()
See gh-32524
2024-03-24 08:29:22 +01:00
Sam Brannen 2a74fe5ddb Introduce null-safe index operator in SpEL
This set of commits introduces support for a null-safe operator in the
Spring Expression Language (SpEL), including support for compiling
expressions that use the null-safe index operator.

Note, however, that compilation is not supported for null-safe indexing
into a String or any kind of Collection (other than a List).

See gh-21468
Closes gh-29847
2024-03-23 15:03:51 +01:00
Sam Brannen 218a148898 Document null-safe index operator in SpEL
See gh-29847
2024-03-23 14:56:01 +01:00
Sam Brannen 38c473fd05 Support compilation of null-safe index operations in SpEL
See gh-29847
2024-03-23 13:48:20 +01:00
Sam Brannen d2bd0d5716 Retain null-safe syntax in AST representation of SpEL indexers
Prior to this commit, SpEL's CompoundExpression omitted the null-safe
syntax in AST string representations of indexing operations.

To address this, this commit implements isNullSafe() in Indexer.

See gh-29847
2024-03-23 12:46:03 +01:00
Sam Brannen 4d433174eb Revise null-safe index operator support in SpEL
See gh-29847
2024-03-23 12:46:03 +01:00
Grigory Stepanov 9f4d46fe33 Introduce null-safe index operator in SpEL
See gh-29847
2024-03-23 12:38:10 +01:00
Sam Brannen 2a1abb5553 Simplify compilation of array indexing in SpEL's Indexer 2024-03-23 12:35:25 +01:00
Sam Brannen 7edd4e8e22 Clean up warnings in Gradle build 2024-03-23 09:28:49 +01:00
Felix 05b15812bb Apply instanceof pattern matching in RootBeanDefinition
Closes gh-32520
2024-03-23 09:09:25 +01:00
Sébastien Deleuze 0e7aba4179 Perform NullAway build-time checks in spring-webmvc
See gh-32475
2024-03-22 17:49:03 +01:00
Sébastien Deleuze cf8810042a Merge branch '6.1.x' 2024-03-22 17:46:35 +01:00
Sam Brannen 88bc3f270e Merge branch '6.1.x' 2024-03-22 17:18:38 +01:00
Sam Brannen b695dbc2bf Merge branch '6.1.x' 2024-03-22 16:37:55 +01:00
Stéphane Nicoll cf4ac96688 Merge branch '6.1.x' 2024-03-22 16:33:17 +01:00
Sébastien Deleuze 0a50854e1a Perform NullAway build-time checks in spring-webflux
See gh-32475
2024-03-22 16:09:18 +01:00
Sébastien Deleuze 637aa9f71f Merge branch '6.1.x' 2024-03-22 16:06:25 +01:00
Sébastien Deleuze b1b9ee0ccf Merge branch '6.1.x' 2024-03-21 17:56:14 +01:00
Sam Brannen a30c06b883 Polishing and consistent use of exception assertions 2024-03-21 16:36:14 +01:00
Andrea Mauro 458c30cb63 Resolve property-dependent parameter names for exception messages
Prior to this commit when a required parameter defined as a property or
expression placeholder was missing, the exception thrown would refer to
the placeholder instead of the resolved name.

This change covers messaging handlers and web controllers, both blocking
and reactive. It also fixes the error message when handling null values
for non-required parameters, as well as in cases that need conversion.

See gh-32323
Closes gh-32462
2024-03-20 17:40:38 +01:00
Sam Brannen 5698191ba0 Rename listener to CommonCachesTestExecutionListener
See gh-30954
2024-03-20 16:37:22 +01:00
Sam Brannen 7d197972e0 Polishing 2024-03-20 13:05:30 +01:00
Sam Brannen 1732844137 Polishing 2024-03-20 12:55:35 +01:00
Sam Brannen 7f40b49f4d Improve names of classes generated by the SpEL compiler
Prior to this commit, the SpEL compiler generated classes in a package
named "spel" with names following the pattern "Ex#", where # was an
index starting with 2.

This resulted in class names such as:

- spel.Ex2
- spel.Ex3

This commit improves the names of classes created by the SpEL compiler
by generating classes in a package named
"org.springframework.expression.spel.generated" with names following
the pattern "CompiledExpression#####", where ##### is a 0-padded
counter starting with 00001.

This results in class names such as:

- org.springframework.expression.spel.generated.CompiledExpression00001
- org.springframework.expression.spel.generated.CompiledExpression00002

This commit also moves the saveGeneratedClassFile() method from
SpelCompilationCoverageTests to SpelCompiler and enhances it to:

- Save classes in a "build/generated-classes" directory.
- Convert package names to directories.
- Create missing parent directories.
- Use logging instead of System.out.println().

Running a test with saveGeneratedClassFile() enabled now logs something
similar to the following.

DEBUG o.s.e.s.s.SpelCompiler - Saving compiled SpEL expression [(#root.empty ? 0 : #root.size)] to [/Users/<username>/spring-framework/spring-expression/build/generated-classes/org/springframework/expression/spel/generated/CompiledExpression00001.class]

Closes gh-32497
2024-03-20 12:34:01 +01:00
Stéphane Nicoll 2f070e59e6 Merge branch '6.1.x' 2024-03-20 10:16:26 +01:00
Sébastien Deleuze f648fd7c3b Perform NullAway build-time checks in spring-expression
See gh-32475
2024-03-20 10:10:00 +01:00
Sébastien Deleuze 1ccd5512c5 Merge branch '6.1.x' 2024-03-20 10:08:30 +01:00
Stéphane Nicoll 5a86ab99df Merge branch '6.1.x' 2024-03-20 10:01:56 +01:00
Stéphane Nicoll 29d307e211 Clear ApplicationContext cache after a test class completes if necessary
The ApplicationContext has a resource cache that it uses while the
context is being refreshed. Once the refresh phase completes, the
cache is cleared as its content is no longer in use. If beans are
lazily initialized, there is a case where the cache might be filled
again as processing is deferred past the refresh phase.

For those cases, the cache is not cleared. This can be a problem when
running in this mode with a large set of test configurations as the TCF
caches the related contexts.

This commit includes an additional TestExecutionListener to the TCF that
clears the resource cache if necessary once a test class completes. It
is ordered so that it runs after `@DirtiesContext` support as marking
the context as dirty will remove it from the cache and make that extra
step unnecessary.

Closes gh-30954
2024-03-20 09:42:41 +01:00
Sébastien Deleuze 4c7735016b Refine null-safety with NullAway build-time checks
This commit introduces null-safety checks for spring-core at build-time
in order to validate the consistency of Spring null-safety annotations
and generate errors when inconsistencies are detected during a build
(similar to what is done with Checkstyle).

In order to make that possible, this commit also introduces a new
org.springframework.lang.Contract annotation inspired from
org.jetbrains.annotations.Contract, which allows to specify semantics
of methods like Assert#notNull in order to prevent artificial
additional null checks in Spring Framework code base.

This commit only checks org.springframework.core package, follow-up
commits will also extend the analysis to other modules, after related
null-safety refinements.

See gh-32475
2024-03-19 16:30:32 +01:00
Sam Brannen 0a715bcab3 Merge branch '6.1.x' 2024-03-19 15:52:12 +01:00
Sébastien Deleuze 8d23ec4747 Merge branch '6.1.x' 2024-03-19 13:36:39 +01:00
Juergen Hoeller 14bc0d6469 Fix typo in condition check
See gh-21190
2024-03-19 13:17:33 +01:00
Sam Brannen 3bd342ee7d Polish bean override support in the TestContext framework 2024-03-19 12:41:06 +01:00
Sébastien Deleuze 871860798e Merge branch '6.1.x' 2024-03-19 11:05:22 +01:00
Juergen Hoeller e58ea0d945 Merge branch '6.1.x'
# Conflicts:
#	spring-core/src/main/java/org/springframework/core/CoroutinesUtils.java
2024-03-19 10:06:48 +01:00
Sam Brannen 8f56b4a8ae Fix logic error hidden by auto-boxing 2024-03-18 18:19:34 +01:00
Sam Brannen 2ca10e20ca Simplify implementation of BeanOverrideParser
See gh-29917
2024-03-18 18:13:36 +01:00
Sam Brannen 0ef5a4090b Support non-public BeanOverrideProcessors in the TestContext framework
Closes gh-32485
2024-03-18 18:02:51 +01:00
Juergen Hoeller 6df27644eb Merge branch '6.1.x' 2024-03-18 16:04:26 +01:00
Juergen Hoeller 2f2c4188e5 Nullability refinements
See gh-32475
2024-03-18 15:49:20 +01:00
Sam Brannen ccebaa6b0c Merge branch '6.1.x' 2024-03-18 15:18:06 +01:00
Brian Clozel ae5cff1705 Merge branch '6.1.x' 2024-03-18 14:37:21 +01:00
Stéphane Nicoll 3e102e5d8e Merge branch '6.1.x' 2024-03-18 14:11:02 +01:00
Brian Clozel 9cd4d32195 Merge branch '6.1.x' 2024-03-18 12:18:29 +01:00
Stéphane Nicoll 73a1c2d509 Polish 2024-03-18 08:59:22 +01:00
Juergen Hoeller 2b56ca08d4 Restore canonical name representation for 6.2
See gh-32405
2024-03-17 21:03:02 +01:00
Juergen Hoeller b89cf2b16a Merge branch '6.1.x' 2024-03-17 21:00:00 +01:00
Johnny Lim 99e1b0d117 Make SimpleAliasRegistry.aliasNames final 2024-03-17 13:33:31 +01:00
Juergen Hoeller 89d563097f Merge branch '6.1.x'
# Conflicts:
#	spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java
2024-03-16 23:24:51 +01:00
Brian Clozel 10e8039de8 Merge branch '6.1.x' 2024-03-15 22:48:12 +01:00
Juergen Hoeller 2086cda296 Merge branch '6.1.x' 2024-03-15 21:30:55 +01:00
Juergen Hoeller 66235fa8c8 Consistent TaskDecorator and ErrorHandler support in schedulers
Closes gh-23755
Closes gh-32460
2024-03-15 21:07:59 +01:00
Sam Brannen cf31d088e2 Polish AssertJ support for MockMvc
See gh-21178
2024-03-15 17:15:32 +01:00
Stéphane Nicoll 4a74e1fc2d Add AssertJ support for MockMvc
Closes gh-21178
2024-03-15 13:32:49 +01:00
Stéphane Nicoll e7d7cb8641 Add AssertJ support for MockMvc
This commit adds a new way to use MockMvc by returning a MvcResult that
is AssertJ compatible. Compared to the existing MockMvc infrastructure,
this new model has the following advantages:

* Can be created from a MockMvc instance
* Dedicated builder methods for easier setup
* Assertions use familiar AssertJ syntax with discoverable assertions
through a DSL
* Improved exception message

See gh-21178

Co-authored-by: Brian Clozel <brian.clozel@broadcom.com>
2024-03-15 13:30:32 +01:00
Stéphane Nicoll 555d4a6004 Add AssertJ support for cookies
This commit adds AssertJ compatible assertions for cookies

See gh-21178

Co-authored-by: Brian Clozel <brian.clozel@broadcom.com>
2024-03-15 13:28:12 +01:00
Stéphane Nicoll 1cdbcc58f3 Add AssertJ support for the Model
This commit adds AssertJ compatible assertions for the Model that is
generated from an HTTP request.

See gh-21178
2024-03-15 13:28:12 +01:00
Stéphane Nicoll b46e528922 Add AssertJ support for the HTTP handler
This commit adds AssertJ compatible assertions for the component that
produces the result from the request.

See gh-21178
2024-03-15 13:28:12 +01:00
Stéphane Nicoll e97ae434fb Add AssertJ support for HTTP requests and responses
This commit adds AssertJ compatible assertions for HTTP request and
responses, including headers, media type, and response body. The latter
delegate to the recently included JSON assert support.

See gh-21178
2024-03-15 13:28:11 +01:00
Stéphane Nicoll 76f45c4289 Add support for JSON assertions using JSON path
This commit moves JSON path AssertJ support from Spring Boot.

See gh-21178

Co-authored-by: Brian Clozel <brian.clozel@broadcom.com>
2024-03-15 13:28:11 +01:00
Stéphane Nicoll 97ebc43ea9 Add support for JSON assertions using JSON compare
This commit moves JSON content AssertJ support from Spring Boot.

See gh-21178

Co-authored-by: Brian Clozel <brian.clozel@broadcom.com>
2024-03-15 13:28:11 +01:00
Stéphane Nicoll 214a54dd6f Run CI on main only once a day 2024-03-15 08:40:09 +01:00
Stéphane Nicoll 875037c431 Merge branch '6.1.x' 2024-03-15 08:28:24 +01:00
Stéphane Nicoll 35e8ae2a84 Merge branch '6.1.x' 2024-03-14 17:47:51 +01:00
Stéphane Nicoll 26cab0616e Merge branch '6.1.x' 2024-03-14 17:43:32 +01:00
Sam Brannen e1b1435a00 Stop referring to JDK 1.5 plus polishing 2024-03-14 16:00:51 +01:00
Stéphane Nicoll 8e2134c6a7 Merge branch '6.1.x' 2024-03-14 15:29:18 +01:00
Stéphane Nicoll 7c7a59bdd2 Merge branch '6.1.x' 2024-03-14 15:17:47 +01:00
Stéphane Nicoll acd69d38e9 Merge branch '6.1.x' 2024-03-14 15:05:46 +01:00
Sam Brannen 4a3daa7812 Polishing 2024-03-14 14:20:59 +01:00
Sam Brannen 27d5213176 Polish Javadoc for @⁠TestBean support 2024-03-14 13:44:27 +01:00
Stéphane Nicoll 19dfc781f5 Build Pull Requests on CI using GitHub Actions
Closes gh-32443
2024-03-14 11:10:41 +01:00
rstoyanchev 1d2b55e670 Merge branch '6.1.x' 2024-03-14 08:46:46 +00:00
Juergen Hoeller 7f7c0d59f6 Merge branch '6.1.x' 2024-03-13 18:07:21 +01:00
Stéphane Nicoll 7d4db434a5 Merge branch '6.1.x' 2024-03-13 17:28:23 +01:00
Sam Brannen 986c4fd926 Revise use of Objects.requireNonNull()
Historically, we have rarely intentionally thrown a
NullPointerException in the Spring Framework. Instead, we prefer to
throw either an IllegalArgumentException or IllegalStateException
instead of a NullPointerException.

However, changes to the code in recent times have introduced the use of
Objects.requireNonNull(Object) which throws a NullPointerException
without an explicit error message.

The latter ends up providing less context than a NullPointerException
thrown by the JVM (since Java 14) due to actually de-referencing a
null-pointer. See https://openjdk.org/jeps/358.

In light of that, this commit revises our current use of
Objects.requireNonNull(Object) by removing it or replacing it with
Assert.notNull().

However, we still use Objects.requireNonNull(T, String) in a few places
where we are required to throw a NullPointerException in order to
comply with a third-party contract such as Reactive Streams.

Closes gh-32430
2024-03-13 16:00:22 +01:00
Sam Brannen d6422d368a Revise @⁠TestBean support
See gh-29917
2024-03-13 16:00:22 +01:00
Sam Brannen 21ed8aad74 Add missing test 2024-03-13 15:59:28 +01:00
Sam Brannen 7211db9262 Polishing 2024-03-13 15:59:28 +01:00
Juergen Hoeller 3b87c87a33 Merge branch '6.1.x' 2024-03-12 20:11:21 +01:00
Sébastien Deleuze 460ffbc0f6 Use code includes and tabs in mvc-controller/ann.adoc
See gh-22171
2024-03-12 19:18:42 +01:00
Sébastien Deleuze 8a67018e61 Use code includes and tabs in embedded-database-support.adoc
See gh-22171
2024-03-12 17:46:38 +01:00
Sébastien Deleuze 68ae1e5700 Fix a link in MockitoResetTestExecutionListener javadoc 2024-03-12 17:43:10 +01:00
Brian Clozel 4b732d62c2 Deprecate HttpHeaders.writableHttpHeaders
Prior to this commit, gh-21783 introduced `ReadOnlyHttpHeaders` to avoid
parsing media types multiple times during the lifetime of an HTTP
exchange: such values are cached and the headers map is made read-only.
This also added a new `HttpHeaders.writableHttpHeaders` method to unwrap
the read-only variant when needed.

It turns out this method sends the wrong signal to the community
because:
* the underlying map might be unmodifiable even if this is not an
  instance of ReadOnlyHttpHeaders
* developers were assuming that modifying the collection that backs the
  read-only instance would work around the cached values for
  Content-Type and Accept headers

This commit adds more documentation to highlight the desired behavior
for cached values by the read-only variant, and deprecates the
`writableHttpHeaders` method as `ReadOnlyHttpHeaders` is package private
and we should not surface that concept anyway.
Instead, this commit unwraps the read-only variant if needed when a new
HttpHeaders instance is created.

Closes gh-32116
2024-03-12 14:04:09 +01:00
rstoyanchev 670fc9bb4e Merge branch '6.1.x' 2024-03-12 11:43:43 +00:00
Sébastien Deleuze 7a65aad09c Merge branch '6.1.x' 2024-03-12 12:24:30 +01:00
Sébastien Deleuze b1bf8c5242 Prevent NPE when using pathExtension predicate
This commit ensures pathExtension predicate is skipped when the value
is null, in order to provide a more predictable behavior, and allow
a better compatibility with collections not supporting null elements
like the ones created by List#of.

Closes gh-32404
2024-03-12 12:11:50 +01:00
rstoyanchev 6767f7010c Polishing contribution
Closes gh-29958
2024-03-12 10:43:32 +00:00
rstoyanchev 8af1d8e842 Remove applyAttributes flag from contribution
See gh-29958
2024-03-12 10:43:31 +00:00
PhilKes 052b6357c8 Apply attributes to the underlying HTTP request
See gh-29958
2024-03-12 10:43:31 +00:00
rstoyanchev 282ee02419 Polishing contribution
Closes gh-31924
2024-03-12 10:43:31 +00:00
Anton Ždanov 667e30f580 Set UTF-8 as default multipart charset to ContentRequestMatchers
See gh-31924
2024-03-12 10:43:31 +00:00
rstoyanchev f9883d8bd6 Polishing contribution
Closes gh-31970
2024-03-12 10:43:31 +00:00
rstoyanchev 73ee86c666 Split messages only if configured to do so
See gh-31970
2024-03-12 10:43:31 +00:00
injae-kim 76d00d78db Support splitting STOMP messages in WebSocketStompClient
See gh-31970
2024-03-12 10:43:31 +00:00
Brian Clozel bf014ef18b Merge branch '6.1.x' 2024-03-12 09:52:22 +01:00
Stéphane Nicoll 13679bb906 Reject use of component scan with REGISTER_BEAN condition
This commit introduce a change of behaviour when component scan is used
with conditions. Previously, any condition in the REGISTER_BEAN phase
were ignored and the scan was applied regardless of the outcome of
those conditions. This is because REGISTER_BEAN condition evaluation
happens later in the bean factory preparation.

Rather than ignoring those conditions, this commit fails fast when it
detects such use case. Code will have to be adapted accordingly.

Closes gh-23206
2024-03-12 08:39:53 +01:00
Stéphane Nicoll 6fc1f72704 Merge branch '6.1.x' 2024-03-12 08:21:08 +01:00
Sam Brannen 04e69bdb26 Polish contribution
Closes gh-32412
2024-03-11 17:04:53 +01:00
Mikaël Francoeur 3e48031601 Reject null return value from MethodReplacer for primitive return type
This commit throws an exception instead of silently converting a null
return value from a MethodReplacer to a primitive 0/false value.

See gh-32412
2024-03-11 17:04:53 +01:00
Sam Brannen f285971cb3 Polishing 2024-03-11 17:04:53 +01:00
Stéphane Nicoll 89f6e8ec49 Enable test now that the related issue is resolved
See gh-20765
2024-03-11 16:45:30 +01:00
Stéphane Nicoll 53ed15e4a7 Remove disabled test of declined issue
See gh-19689
2024-03-11 16:44:50 +01:00
Brian Clozel 9af4f5cf17 Remove deprecated API in WebContentGenerator
Closes gh-31492
2024-03-11 15:35:12 +01:00
Stéphane Nicoll 92d1ebefbb Upgrade to HtmUnit 3.11.0
This commit upgrades to a major new release of HtmlUnit. This is a
breaking change as HtmlUnit 3 moves to a `org.htmlunit` package and
calling code needs to be restructured.

Our use of Selenium has been adapted accordingly, moving to Selenium
3, using the new org.seleniumhq.selenium:htmlunit3-driver integration.

Closes gh-30392
2024-03-11 14:36:46 +01:00
Sam Brannen 71245f9655 Relocate bean override infrastructure to the proper package in the TCF 2024-03-11 14:21:04 +01:00
Sam Brannen f30a67c9d0 Remove duplicate dependency 2024-03-11 13:59:23 +01:00
Sébastien Deleuze 0435e9c49e Merge branch '6.1.x' 2024-03-11 13:40:23 +01:00
Sam Brannen 38c831f15f Relocate findPublicDeclaringClass() to CodeFlow
This commit moves findPublicDeclaringClass() from ReflectionHelper to
CodeFlow, since findPublicDeclaringClass() is only used for bytecode
generation and therefore not for reflection-based invocations.
2024-03-11 11:33:52 +01:00
Sam Brannen c4e0f96ef7 Polishing 2024-03-11 11:28:59 +01:00
Sébastien Deleuze 5ee11fb1b3 Polish Yaml support
Closes gh-32345
2024-03-11 09:27:25 +01:00
Hyoungjune 6a8f0d6d7d Add web support for Yaml via Jackson
This commit adds support for application/yaml in MediaType and leverages
jackson-dataformat-yaml in order to support Yaml in RestTemplate,
RestClient and Spring MVC.

See gh-32345
2024-03-11 09:22:51 +01:00
Yanming Zhou 246e4977a2 Polishing Optional usage 2024-03-11 09:13:49 +01:00
Stéphane Nicoll 4e5723ca9e Merge pull request #32411 from quaff
* pr/32411:
  Polish contribution
  Polish Bean Overriding in Tests section of the reference guide

Closes gh-32411
2024-03-11 08:57:57 +01:00
Stéphane Nicoll ffb1caaff4 Polish contribution
See gh-32411
2024-03-11 08:56:59 +01:00
Yanming Zhou 918c5f1f33 Polish Bean Overriding in Tests section of the reference guide
See gh-32411
2024-03-11 08:43:40 +01:00
Sam Brannen b431594021 Make SpEL's OptimalPropertyAccessor private
It was never necessary for SpEL's OptimalPropertyAccessor to be public.

Instead, clients should interact with OptimalPropertyAccessor
exclusively via the CompilablePropertyAccessor API.

In light of that, this commit makes SpEL's OptimalPropertyAccessor
private.

Closes gh-32410
2024-03-10 20:38:19 +01:00
Sam Brannen fc885224f0 Merge branch '6.1.x' 2024-03-10 17:41:49 +01:00
ZeroCyan 5dc1190930 Fix typo in web documentation
Fixed a small typo.

Closes gh-32407
2024-03-10 16:50:00 +01:00
Sam Brannen 6f5d3a4b12 Polish Bean Override support in the TestContext framework 2024-03-10 16:05:55 +01:00
Sam Brannen 4c246b7c96 Consistently use canonical annotation names in string representations 2024-03-10 15:54:53 +01:00
Sam Brannen 5345a13918 Polish contribution
See gh-32405
2024-03-10 12:36:08 +01:00
Patrick Strawderman 7fa2a28970 Avoid cloning empty Annotation array in TypeDescriptor
Rework AnnotatedElementAdapter to avoid cloning the underlying
Annotation array if it is empty when getAnnotations() is called.

Additionally, make the class static and add a factory method that
returns a singleton instance for null or empty Annotation arrays.

Closes gh-32405
2024-03-10 12:26:49 +01:00
Stéphane Nicoll 8172d7adfe Polish 2024-03-09 17:27:44 +01:00
Sam Brannen 6b64e9992a Merge branch '6.1.x' 2024-03-09 16:31:28 +01:00
Sam Brannen c188f22378 Polishing 2024-03-09 16:08:17 +01:00
Stéphane Nicoll e53ed3e3c6 Merge branch '6.1.x' 2024-03-09 16:03:04 +01:00
Sam Brannen f4c1ad7ae6 Polishing
See gh-29857
2024-03-09 15:38:52 +01:00
Sam Brannen 20be9e150c Polishing 2024-03-09 14:28:01 +01:00
Sam Brannen 65d77624d1 Support SpEL compilation for public methods in private subtypes
Commit c79436f832 ensured that methods are invoked via a public
interface or public superclass when compiling Spring Expression
Language (SpEL) expressions involving method references or property
access (see MethodReference, PropertyOrFieldReference, and
collaborating support classes). However, compilation of expressions
that access properties by indexing into an object by property name is
still not properly supported in all scenarios.

To address those remaining use cases, this commit ensures that methods
are invoked via a public interface or public superclass when accessing
a property by indexing into an object by the property name – for
example, `person['name']` instead of `person.name`.

In addition, SpEL's Indexer now properly relies on the
CompilablePropertyAccessor abstraction instead of hard-coding support
for only OptimalPropertyAccessor. This greatly reduces the complexity
of the Indexer and simultaneously allows the Indexer to potentially
support other CompilablePropertyAccessor implementations.

Closes gh-29857
2024-03-09 14:01:00 +01:00
Sam Brannen 107f47cfcf Add tests for status quo for SpEL compiler 2024-03-09 13:46:39 +01:00
Juergen Hoeller fad544e077 Merge branch '6.1.x'
# Conflicts:
#	spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java
2024-03-08 19:51:05 +01:00
Juergen Hoeller 19fec0633f Local root directory and jar caching in PathMatchingResourcePatternResolver
Closes gh-21190
2024-03-08 19:12:14 +01:00
Simon Baslé 729dc0b671 Polishing javadoc 2024-03-08 18:07:51 +01:00
rstoyanchev 38d5c0fed6 Add RFC-7807 response interception
Closes gh-31822
2024-03-08 16:56:38 +00:00
Sam Brannen 3577e3b758 Polish SpEL internals and tests 2024-03-08 17:49:58 +01:00
Simon Baslé e1bbdf0913 Add support for bean overriding in tests
This commit introduces two sets of annotations (`@TestBean` on one side
and `MockitoBean`/`MockitoSpyBean` on the other side), as well as an
extension mecanism based on the `@BeanOverride` meta-annotation.

Extension implementors are expected to only provide an annotation,
a BeanOverrideProcessor implementation and an OverrideMetadata subclass.

Closes gh-29917.
2024-03-08 16:52:39 +01:00
Sam Brannen 90867e7e62 Merge branch '6.1.x' 2024-03-08 15:50:29 +01:00
Sébastien Deleuze 5cac2ca11a Fix code snippet compilation error
See gh-22171
2024-03-08 12:28:37 +01:00
Sébastien Deleuze db3c7a157e Use consistently snippet tags
See gh-22171
2024-03-08 12:15:51 +01:00
Sébastien Deleuze d2e55a2038 Use code includes and tabs in jdbc/core.adoc
See gh-22171
2024-03-08 12:15:51 +01:00
Sam Brannen ae6c64abc5 Fix Javadoc errors 2024-03-08 11:59:53 +01:00
Brian Clozel c1bd9243ee Merge branch '6.1.x' 2024-03-08 11:36:54 +01:00
Sébastien Deleuze e66d965fed Merge branch '6.1.x' 2024-03-08 09:25:17 +01:00
Sébastien Deleuze aa4282d7f8 Polishing
See gh-22171
2024-03-07 18:16:57 +01:00
Sébastien Deleuze 34f0c27b5e Fix build link 2024-03-07 17:42:49 +01:00
Sébastien Deleuze 722199fe6d Use code includes and tabs in connections.adoc
See gh-22171
2024-03-07 17:40:11 +01:00
Sébastien Deleuze 9cb8766977 Add support for Kotlin and XML code includes
See gh-22171
2024-03-07 17:37:48 +01:00
rstoyanchev 8d24e22e33 Merge branch '6.1.x' 2024-03-07 16:06:18 +00:00
Sam Brannen c79436f832 Support SpEL compilation for public methods in private subtypes
Although the Spring Expression Language (SpEL) generally does a good
job of locating the public declaring class or interface on which to
invoke a method in a compiled expression, prior to this commit there
were still a few unsupported use cases.

To address those remaining use cases, this commit ensures that methods
are invoked via a public interface or public superclass whenever
possible when compiling SpEL expressions.

See gh-29857
2024-03-07 16:20:50 +01:00
rstoyanchev 1ffffef85e Merge branch '6.1.x' 2024-03-07 14:48:41 +00:00
Sébastien Deleuze f0039833c8 Merge branch '6.1.x' 2024-03-07 14:31:26 +01:00
Sam Brannen ac1176af53 Use Map#computeIfAbsent in SpEL support classes
Closes gh-32385
2024-03-07 13:55:35 +01:00
Sam Brannen dcbc2ef134 Polishing 2024-03-07 13:55:35 +01:00
Juergen Hoeller 4ec4e93ece Document when the JPA infrastructure is ready for use
Closes gh-26153
See gh-21868
2024-03-07 13:35:58 +01:00
Simon Baslé 7f0ab22c47 Merge branch '6.1.x' 2024-03-07 12:10:07 +01:00
Sébastien Deleuze 92f4e883b3 Merge branch '6.1.x' 2024-03-07 11:11:46 +01:00
Juergen Hoeller dedc6a7e9b Enforce JPA/Hibernate initialization before context refresh completion
Closes gh-21868
2024-03-07 11:03:08 +01:00
Juergen Hoeller c9e85ec297 Introduce callback for singleton availability
Closes gh-21362
2024-03-07 09:57:29 +01:00
rstoyanchev 1a8d64f9d8 Merge branch '6.1.x' 2024-03-06 18:17:40 +00:00
Juergen Hoeller 13221ac0ef Merge branch '6.1.x'
# Conflicts:
#	spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java
#	spring-tx/src/test/java/org/springframework/transaction/interceptor/TransactionInterceptorTests.java
2024-03-06 18:09:32 +01:00
Sam Brannen 40da093f58 Polishing 2024-03-06 15:42:14 +01:00
Sam Brannen b32a2cadfb Update class-level Javadoc for ParameterNameDiscoverer 2024-03-06 14:49:46 +01:00
Sam Brannen d87465f9e9 Use ELContext instead of VariableResolver in JspPropertyAccessor
The JSP VariableResolver API has been deprecated since JSP 2.1 in favor
of the newer ELContext API.

This commit therefore refactors JspPropertyAccessor to use the
ELContext API.

Closes gh-32383
2024-03-06 14:31:21 +01:00
Juergen Hoeller 14a461e795 Consider type-level qualifier annotations for transaction manager selection
Closes gh-24291
2024-03-06 13:36:33 +01:00
Sam Brannen 6461eec582 Revise contribution
Closes gh-32380
2024-03-06 11:49:21 +01:00
Yanming Zhou be136d79ee Improve Javadoc for NamedParameterUtils.substituteNamedParameters()
This commit documents that an empty list is not guaranteed to be
supported by the database. For example, MySQL and PostgreSQL do not
support `foo in ()`.

See gh-32380
2024-03-06 11:49:15 +01:00
Sam Brannen 9eea768205 Polish SpEL internals 2024-03-06 11:31:10 +01:00
Simon Baslé 19b5f11734 Merge branch '6.1.x' 2024-03-05 18:33:48 +01:00
Juergen Hoeller 6f7f032ced Merge branch '6.1.x' 2024-03-05 18:24:01 +01:00
Juergen Hoeller 7d4c8a403e Introduce configurable default rollback rules
Includes rollbackOn annotation attribute on @EnableTransactionManagement and addDefaultRollbackRule method on AnnotationTransactionAttributeSource, as well as publicMethodsOnly as instance-level flag (also on AnnotationCacheOperationSource).

Closes gh-23473
2024-03-05 18:08:08 +01:00
ali dandach eb01cc0d9d Use String#isEmpty where feasible
This commit replaces checks for empty strings ("".equals(...)) with the
String#isEmpty method.

Closes gh-32377
2024-03-05 17:38:29 +01:00
Juergen Hoeller 30e75e4a09 Enable efficient pointcut checks for composite sources as well
Closes gh-20072
2024-03-05 13:34:48 +01:00
Sam Brannen 1fa6ac30b5 Remove unused lastReadInvokerPair field in ReflectivePropertyAccessor 2024-03-05 12:55:30 +01:00
rstoyanchev ddab971fca Merge branch '6.1.x' 2024-03-05 11:42:17 +00:00
Sam Brannen b44c31e997 Polishing 2024-03-05 12:01:55 +01:00
Sam Brannen 1ea593e777 Convert PropertyCacheKey to a record 2024-03-05 12:01:55 +01:00
Sam Brannen 70a545e13a Use appropriate variable names in ReflectivePropertyAccessor 2024-03-05 11:52:30 +01:00
Sam Brannen c29c67839b Cache parameterTypes in ClassUtils.getInterfaceMethodIfPossible 2024-03-05 11:48:01 +01:00
Sébastien Deleuze f6dfbac582 Merge branch '6.1.x' 2024-03-05 11:01:29 +01:00
Arjen Poutsma 6d9aba88d0 Fix typo
See gh-32360
2024-03-05 10:48:06 +01:00
Sébastien Deleuze db826551a6 Upgrade to Dokka 1.9.20
Closes gh-32374
2024-03-05 10:31:29 +01:00
Arjen Poutsma 646bd7f893 Document StringUtils::uriDecode limitations
Closes gh-32360
2024-03-05 10:03:15 +01:00
ali dandach 1cb2dfa459 enh: use isEmpty() instead of length() 2024-03-04 23:55:33 +01:00
Juergen Hoeller 79c54098ab Merge branch '6.1.x' 2024-03-04 23:49:55 +01:00
Juergen Hoeller dc6c96de0a Merge branch '6.1.x'
# Conflicts:
#	spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractFallbackJCacheOperationSource.java
#	spring-context/src/main/java/org/springframework/cache/interceptor/AbstractFallbackCacheOperationSource.java
#	spring-tx/src/main/java/org/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.java
2024-03-04 22:50:22 +01:00
Juergen Hoeller b5ca646431 Leniently tolerate late bean retrieval during destroySingletons()
Closes gh-22526
Closes gh-29730
2024-03-04 17:21:02 +01:00
Sébastien Deleuze 138e7a0e07 Use ServletResponse#getContentType in ServletServerHttpResponse
This commit updates ServletServerHttpResponse.ServletResponseHttpHeaders
in order to use ServletResponse#getContentType instead of
ServletResponse#getHeader.

It allows to have a consistent behavior between Tomcat (which sets only
the former) and Undertow/Jetty (which set both).

Closes gh-32339
2024-03-04 15:32:32 +01:00
Sébastien Deleuze 4ce2559980 Merge branch '6.1.x' 2024-03-04 14:43:18 +01:00
rstoyanchev 1c5bb732b9 Merge branch '6.1.x' 2024-03-04 13:36:30 +00:00
Juergen Hoeller 3d7ef3ebfc Avoid storage of null marker per method for proxy decision purposes
Includes missing isCandidateClass support on JCacheOperationSource.

Closes gh-20072
2024-03-04 12:55:33 +01:00
Sébastien Deleuze 219004ef13 Merge branch '6.1.x' 2024-03-03 22:26:37 +01:00
rstoyanchev 0758c8b14c Merge branch '6.1.x' 2024-03-03 20:53:36 +00:00
Sam Brannen 4a6dbb17f4 Merge branch '6.1.x' 2024-03-03 14:51:01 +01:00
Sam Brannen a29c84f663 Merge branch '6.1.x' 2024-03-02 18:08:27 +01:00
Juergen Hoeller 390fe0fe78 Add support for resolving multiple bounds in type variables
Closes gh-22902
See gh-32327
2024-03-02 11:30:17 +01:00
Stéphane Nicoll ac1a030c35 Make PlaceholderResolutionException extend from IllegalArgumentException
To smooth upgrade from 6.1.x, this commit makes sure that code that used
to catch an IAE to ignore a faulty placeholder resolution still works.

See gh-9628
2024-03-02 08:28:38 +01:00
Sam Brannen fdbefad59c Improve documentation for SpEL indexing support
Prior to this commit, the reference manual only documented indexing
support for arrays, lists, and maps.

This commit improves the overall documentation for SpEL's property
navigation and indexing support and introduces additional documentation
for indexing into Strings and Objects.

Closes gh-32355
2024-03-01 18:08:29 +01:00
Sébastien Deleuze bdcd10e228 Merge branch '6.1.x' 2024-03-01 16:53:27 +01:00
Juergen Hoeller 861ef88d9f Expose savepoint callbacks on TransactionSynchronization
Closes gh-30509
2024-03-01 16:48:51 +01:00
Sam Brannen 193424c465 Polish "Background Initialization" documentation 2024-03-01 16:14:12 +01:00
Sam Brannen c76d681dc4 Merge branch '6.1.x' 2024-03-01 14:41:49 +01:00
Sébastien Deleuze 4149d5cb2b Merge branch '6.1.x' 2024-03-01 11:55:08 +01:00
Juergen Hoeller 89a10d5c9b Reference documentation for @Bean(bootstrap=BACKGROUND)
See gh-13410
2024-02-29 17:51:31 +01:00
Juergen Hoeller 4ac521607e Reference documentation for @Fallback
See gh-26241
2024-02-29 17:51:12 +01:00
Stéphane Nicoll 57c10a1fac Merge branch '6.1.x' 2024-02-29 17:23:08 +01:00
Arjen Poutsma d81619addd Merge branch '6.1.x' 2024-02-29 15:55:29 +01:00
Yanming Zhou 246ebd24bf Add missing method BeanDefinitionBuilder.setFallback() 2024-02-29 13:34:55 +01:00
Yanming Zhou 9a1e5eb8d7 Add test for @Fallback with BeanFactory.getBean(Class) 2024-02-29 13:33:43 +01:00
Juergen Hoeller 4ff0f86ae2 Merge branch '6.1.x' 2024-02-28 21:46:15 +01:00
Juergen Hoeller e68487a481 Merge branch '6.1.x' 2024-02-28 19:27:07 +01:00
Juergen Hoeller cfb29db278 Initial support for Servlet 6.1
Closes gh-31159
2024-02-28 18:54:36 +01:00
Sébastien Deleuze 570c5b34e6 Merge branch '6.1.x' 2024-02-28 18:35:29 +01:00
Juergen Hoeller fbc265b72b Add DataSource configuration/exposure to LocalEntityManagerFactoryBean
Closes gh-32344
2024-02-28 17:35:46 +01:00
Juergen Hoeller 5acee7b22e Initial support for JPA 3.2
Includes proxy support for Query#getSingleResultOrNull() and EntityManagerFactory#getName() invocations.

Closes gh-31157
2024-02-28 17:35:37 +01:00
Sébastien Deleuze 7c5bcbcba4 Merge branch '6.1.x' 2024-02-28 17:24:08 +01:00
Juergen Hoeller 3477738bed Consistently pick lowest superclass level to replace
See gh-28676
2024-02-28 15:49:05 +01:00
Juergen Hoeller fa5d246a1b Replace all exposed superclasses in final step after traversal
See gh-28676
2024-02-28 15:39:53 +01:00
Juergen Hoeller aeb77cf4e1 Restore correct threadWithLock check without isInfoEnabled()
See gh-23501
2024-02-28 11:47:39 +01:00
Sam Brannen 18dc7d73d6 Merge branch '6.1.x' 2024-02-28 10:42:31 +01:00
Juergen Hoeller 17b2087198 Introduce background bootstrapping for individual singleton beans
Closes gh-13410
Closes gh-19487
See gh-23501
2024-02-27 22:33:18 +01:00
Sébastien Deleuze 9e7c64270b Merge branch '6.1.x' 2024-02-27 15:52:13 +01:00
Sam Brannen 69dabe9df5 Merge branch '6.1.x' 2024-02-26 18:12:55 +01:00
Juergen Hoeller 4d206f79d0 Merge branch '6.1.x'
# Conflicts:
#	spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java
2024-02-26 13:42:00 +01:00
Juergen Hoeller 5c589833d7 Revise idleReceivesPerTaskLimit for surplus tasks on default/simple executor
Closes gh-32260
2024-02-25 19:00:56 +01:00
Juergen Hoeller 9198774f13 Detect wildcard without bounds returned from VariableResolver as well
See gh-32327
See gh-20727
2024-02-25 16:32:11 +01:00
Sam Brannen 430824038e Merge branch '6.1.x' 2024-02-25 16:28:13 +01:00
Juergen Hoeller 260404b7f2 Consistently detect wildcard without bounds as unresolvable
Closes gh-32327
See gh-20727
2024-02-24 20:25:56 +01:00
Juergen Hoeller 567547b63c Skip shortcut resolution for non-standard dependency descriptors
Closes gh-32326
See gh-28122
2024-02-24 18:05:23 +01:00
Sam Brannen 98efa1df5a Merge branch '6.1.x' 2024-02-24 16:42:47 +01:00
Juergen Hoeller 87ea3bd31a Merge branch '6.1.x' 2024-02-24 08:35:10 +01:00
Sam Brannen f59c4023e9 Polishing 2024-02-23 14:46:12 +01:00
Juergen Hoeller 58b8330e8d Consistent documentation of defaults and related methods
See gh-32308
2024-02-23 14:41:37 +01:00
Sam Brannen c2d2e99c2f Polishing 2024-02-23 13:18:09 +01:00
Sam Brannen eab1a3dc6b Fix BridgeMethodResolverTests.isBridgeMethodFor() in Eclipse IDE 2024-02-23 12:57:14 +01:00
Sam Brannen 4b5e96578d Extract runningInEclipse() into IdeUtils test fixture 2024-02-23 12:57:06 +01:00
Sam Brannen b0d08fe2d4 Spring cleaning: avoid deprecation warnings 2024-02-23 12:38:40 +01:00
Sam Brannen 122372c580 Spring cleaning: update copyright headers 2024-02-23 12:21:22 +01:00
Sam Brannen 9c610d9a70 Spring cleaning: remove unnecessary semicolon 2024-02-23 12:21:22 +01:00
Sam Brannen 5ae6c0e8ca Spring cleaning: avoid unnecessary super() invocations 2024-02-23 12:21:22 +01:00
Sam Brannen eb8492d597 Spring cleaning: avoid use of Iterator for Iterable 2024-02-23 12:21:12 +01:00
Sam Brannen c98bebd6d3 Spring cleaning: add missing @⁠Override annotations 2024-02-23 12:20:11 +01:00
Sam Brannen d0ffc16efc Spring cleaning: avoid unnecessary static imports 2024-02-23 12:20:11 +01:00
Sam Brannen 4bd1485ce4 Spring cleaning: use method references 2024-02-23 12:20:11 +01:00
Sam Brannen 4339c8eac2 Spring cleaning: use diamond operator 2024-02-23 12:20:11 +01:00
Sam Brannen 233b59f144 Polish Javadoc 2024-02-23 12:20:11 +01:00
Sam Brannen 3c00637c88 Fix @⁠inheritDoc usage 2024-02-23 12:08:29 +01:00
Juergen Hoeller 524588ef93 Avoid transaction listener execution without transaction management setup
Closes gh-32319
2024-02-23 11:53:05 +01:00
yuhangbin bed4d684e6 Polishing 2024-02-23 08:37:46 +01:00
Juergen Hoeller 229214a9b0 Merge branch '6.1.x'
# Conflicts:
#	spring-jdbc/src/main/java/org/springframework/jdbc/JdbcUpdateAffectedIncorrectNumberOfRowsException.java
2024-02-22 17:01:04 +01:00
Sébastien Deleuze 906109e129 Merge branch '6.1.x' 2024-02-22 16:37:58 +01:00
cboy 00edba07ff Polishing Javadoc (#32313) 2024-02-22 15:21:33 +01:00
Arjen Poutsma aee03c5201 Use composite collections in attribute merging
This commit introduces composite collections (i.e. Collection, Set, Map)
and uses these composites in request predicates, where before new
collections were instantiated.

Closes gh-32245
2024-02-22 13:11:20 +01:00
Arjen Poutsma 89d746ddf8 Avoid async dispatch if completed in AsyncServerResponse
This commit checks whether the CompletableFuture<ServerResponse> passed
to AsyncServerResponse.async has been completed, and if so returns a
CompletedAsyncServerResponse that simply delegates to the completed
response, instead of the DefaultAsyncServerResponse that uses async
dispatch.

Closes gh-32223
2024-02-22 11:29:51 +01:00
Juergen Hoeller 3ddc512108 Add missing @Override annotations 2024-02-22 11:21:51 +01:00
Yanming Zhou 76eb5b8c19 Replace redundant javadoc with {@inheritDoc} for AbstractBeanDefinition 2024-02-22 10:59:35 +01:00
Juergen Hoeller 491ebb4ec4 Merge branch '6.1.x'
# Conflicts:
#	spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java
2024-02-21 23:01:09 +01:00
Juergen Hoeller f811f0dc18 Clarify primary/fallback autowiring for arrays/collections/maps
Closes gh-32301
2024-02-21 20:24:57 +01:00
Juergen Hoeller f5397d6426 Consistent processing of overridden bean methods
Closes gh-28286
2024-02-21 18:36:03 +01:00
Brian Clozel 2a1b30dbed Merge branch '6.1.x' 2024-02-21 18:24:45 +01:00
Sam Brannen 734fc476ee Polish SpEL's Indexer and test 2024-02-21 17:44:09 +01:00
Sam Brannen 5a2b127a21 SpEL's StringIndexingValueRef.isWritable() should return false 2024-02-21 17:36:46 +01:00
Sam Brannen bfed6a3bc5 Clean up warnings in Gradle build 2024-02-21 16:22:38 +01:00
Sam Brannen 644887e094 Polish (Linked)HashSet usage
See gh-32291
2024-02-21 16:22:31 +01:00
Sam Brannen b9c304b890 Polish contribution
See gh-32291
2024-02-21 16:19:53 +01:00
Patrick Strawderman e1a32d4ba9 Avoid resizing of fixed-size HashSet/LinkedHashSet variants
Add helpers to CollectionUtils for building HashSets and LinkedHashSets
that can hold an expected number of elements without needing to
resize/rehash.

Closes gh-32291
2024-02-21 16:19:53 +01:00
Stéphane Nicoll 6383a0d7ca Merge branch '6.1.x' 2024-02-21 15:55:55 +01:00
Sam Brannen 3fb170058f Polish contribution
See gh-32294
2024-02-21 14:09:09 +01:00
Patrick Strawderman f9fe8efb2e Use ConcurrentHashMap.newKeySet
In places where a ConcurrentHashMap was used as a set by wrapping it
with Collections.newSetFromMap, switch to just using the set returned
by ConcurrentHashMap.newKeySet directly.

Closes gh-32294
2024-02-21 14:08:38 +01:00
Juergen Hoeller ff9c7141c5 Replace superclass exposure in case of late configuration class skipping
Closes gh-28676
2024-02-21 11:10:27 +01:00
Stéphane Nicoll 871f705bca Remove ComponentScan duplicate condition
Closes gh-27077
2024-02-21 10:09:31 +01:00
Juergen Hoeller 22b41c33ba Preserve existing imported class over scanned configuration class
Closes gh-24643
2024-02-20 20:43:41 +01:00
Juergen Hoeller 266953195c Avoid enhancement of configuration class in case of existing instance
Closes gh-25738
2024-02-20 20:43:09 +01:00
Juergen Hoeller 889c4e0ff5 Reject multiple primary candidates in ancestor factory as well
Closes gh-26612
2024-02-20 16:24:03 +01:00
Juergen Hoeller ad2e95be4b Merge branch '6.1.x' 2024-02-20 15:43:24 +01:00
Juergen Hoeller 63ca8d5d17 Consider defaultCandidate flag in case of no annotations as well
See gh-26528
2024-02-20 15:30:38 +01:00
Juergen Hoeller bc01e3116f Ignore fallback bean for shortcut resolution
See gh-26241
See gh-28122
2024-02-20 13:51:37 +01:00
Juergen Hoeller 480051a21c Introduce fallback flag and annotation (as companion to primary)
Closes gh-26241
2024-02-20 13:37:41 +01:00
Arjen Poutsma c077805761 Set correct capacity in DefaultDataBuffer::setNativeBuffer
Closes gh-30984
2024-02-20 12:36:55 +01:00
Arjen Poutsma ceb7afdca2 Merge pull request #32009 from injae-kim:fix-defaultDataBuffer-getNativeBuffer
* gh-32009:
  Polishing external contribution
  Set correct limit in DefaultDataBuffer::getNativeBuffer
2024-02-20 12:20:22 +01:00
Arjen Poutsma 70004e9ad0 Polishing external contribution
Change position and limit on duplicate, rather than source.

See gh-30967
Closes gh-32009
2024-02-20 12:12:35 +01:00
injae-kim 969d0bd08b Set correct limit in DefaultDataBuffer::getNativeBuffer
Closes gh-30967
2024-02-20 12:12:35 +01:00
Juergen Hoeller a8fb16b47c Introduce defaultCandidate flag (for plain type vs. qualified match)
Closes gh-26528
2024-02-20 12:00:47 +01:00
Arjen Poutsma bc2257aaff Invoke defaultRequest earlier in RestClient and WebClient
Closes gh-32053
2024-02-20 11:21:24 +01:00
Stéphane Nicoll 218957f0e8 Merge branch '6.1.x' 2024-02-20 10:19:41 +01:00
Juergen Hoeller a001319f1f Add additional shortcut for qualifier value matching target bean name
Closes gh-17677
See gh-28122
2024-02-19 22:39:37 +01:00
Juergen Hoeller 874e61a0c6 Test for async event publication before listener initialized
Closes gh-20904
See gh-23501
See gh-25799
2024-02-19 18:18:09 +01:00
Juergen Hoeller 4a02893c31 Avoid early singleton inference outside of original creation thread
See gh-23501
2024-02-19 17:44:30 +01:00
Juergen Hoeller 902e5707a8 Revise singleton registry for lenient locking (fallback instead of deadlock)
Closes gh-23501
2024-02-19 15:49:33 +01:00
Stéphane Nicoll f529386ce2 Merge pull request #31781 from rstoyanchev
* pr/31781:
  Write form data without charset parameter

Closes gh-31781
2024-02-19 15:23:59 +01:00
rstoyanchev b8715811f8 Write form data without charset parameter
This commit updates the content-type for form data to not include the
charset, unless it is different from the default charset.

Some servers don't handle charset parameter and the spec is unclear.

See gh-31781
2024-02-19 15:21:54 +01:00
Stéphane Nicoll 08998376cc Merge branch '6.1.x' 2024-02-19 10:52:17 +01:00
Stéphane Nicoll 6adda31328 Upgrade CI image to Java 22+36 RC 2024-02-19 10:51:16 +01:00
Sam Brannen c6e6a3e44d Link to section in reference manual 2024-02-18 18:19:59 +01:00
Sam Brannen bc2895a30b Support backticks for quoted identifiers in spring-r2dbc
NamedParameterUtils in spring-r2dbc now supports MySQL-style backticks
for quoted identifiers for consistency with spring-jdbc.

See gh-31944
Closes gh-32285
2024-02-17 16:05:40 +01:00
Sam Brannen d86af57e0f Polishing 2024-02-17 16:00:58 +01:00
Sam Brannen d4cde29f75 Clean up TODOs in SpEL 2024-02-17 15:33:05 +01:00
Sam Brannen 5f1e25a61f Polishing 2024-02-17 15:32:43 +01:00
Juergen Hoeller 0ddfe3977a Merge branch '6.1.x' 2024-02-16 22:50:04 +01:00
Juergen Hoeller c6146ea2db Introduce shortcut for declared dependency name matching target bean name
Closes gh-28122
2024-02-16 21:28:04 +01:00
Sam Brannen eefdee7983 Fix syntax in Selection/Projection examples 2024-02-16 18:44:28 +01:00
Sam Brannen ab48ac36e9 Deprecate local variable support in SpEL's internal ExpressionState
Since the Spring Expression Language does not actually support local
variables in expressions, this commit deprecates all public APIs
related to local variables in ExpressionState (namely, the two
enterScope(...) variants that accept local variable data,
setLocalVariable(), and lookupLocalVariable()).

In addition, we no longer invoke `state.enterScope("index", ...)` in
the Projection and Selection AST nodes since the $index local variable
was never accessible within expressions anyway.

See gh-23202
Closes gh-32004
2024-02-16 18:29:00 +01:00
Sam Brannen c1f0faade7 Polish ExpressionState 2024-02-16 18:25:14 +01:00
rstoyanchev cd2a3fdd8d Remove deprecations in HandlerResult
See gh-30608
2024-02-16 16:16:08 +00:00
Sam Brannen 71dfebbfe5 Polishing 2024-02-16 16:45:18 +01:00
Sam Brannen a85bf3185e Remove APIs deprecated for removal in 6.2
This commit removes the following obsolete and deprecated APIs.

- org.springframework.util.Base64Utils

- org.springframework.cache.jcache.interceptor.JCacheOperationSourcePointcut

- org.springframework.http.client.AbstractClientHttpResponse

- org.springframework.http.client.ClientHttpResponse.getRawStatusCode()

- org.springframework.http.client.observation.ClientHttpObservationDocumentation.HighCardinalityKeyNames.CLIENT_NAME

- org.springframework.web.reactive.function.client.ClientHttpObservationDocumentation.HighCardinalityKeyNames.CLIENT_NAME

- org.springframework.web.filter.reactive.ServerWebExchangeContextFilter.get(Context)

- org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleBindException(...)

- org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.handleBindException(...)

Closes gh-30608
2024-02-16 16:45:18 +01:00
Sam Brannen 6d5bf6d9b3 Ensure alias resolution in SimpleAliasRegistry depends on registration order
Closes gh-32024
2024-02-16 15:22:27 +01:00
Sam Brannen 6b482df6fa Clean up warning in Gradle build 2024-02-16 15:04:41 +01:00
Sam Brannen ea4e7df9ca Consistently declare ignoreUnresolvablePlaceholders as last argument 2024-02-16 15:00:32 +01:00
Sam Brannen 7c07c43201 Polishing 2024-02-16 15:00:06 +01:00
Juergen Hoeller 6791ea94a0 Change executor phase to MAX_VALUE/2 and reduce timeout to 10 seconds
Closes gh-32152
2024-02-16 14:16:32 +01:00
Juergen Hoeller 7ee8e66c7f Avoid internal lifecycle synchronization in favor of lifecycle lock
Closes gh-32284
2024-02-16 12:42:39 +01:00
Juergen Hoeller 34372ee32b Introduce lock-based lifecycle waiting and default virtual threads flag
Closes gh-32252
2024-02-16 12:42:27 +01:00
Sam Brannen b6df5a677e Polishing 2024-02-16 11:41:22 +01:00
Sam Brannen fc9a118406 Polish SimpleCommandLinePropertySource support 2024-02-16 11:41:22 +01:00
Sam Brannen 87e6d1b7da Update Eclipse template to 6.2 2024-02-16 11:41:22 +01:00
Juergen Hoeller ea3573176a Avoid infinite recursion for self-referencing generic type
Closes gh-32282
See gh-30079
2024-02-16 11:15:02 +01:00
Stéphane Nicoll bcf235cc66 Merge branch '6.1.x' 2024-02-16 08:36:26 +01:00
Juergen Hoeller 7ad197e8e1 Deprecate LobHandler and SqlXmlHandler abstractions
Including SqlLobValue and related support classes.

Closes gh-32179
2024-02-15 17:26:49 +01:00
Juergen Hoeller 7e67da8a26 Support for matching partial generics
Closes gh-20727
2024-02-15 16:51:09 +01:00
Juergen Hoeller d4e8daaede Add javadoc note on recursive resolution as of 6.2
See gh-30079
2024-02-15 16:50:53 +01:00
Yanming Zhou e788aeb25b Improve GenericTypeResolver to resolve type variable recursively
Fix GH-24963
2024-02-15 16:28:45 +01:00
Stéphane Nicoll 4b2e40153a Merge pull request #32243 from ttddyy
* pr/32243:
  Polish "Add ThreadLocalAccessor for LocaleContext and RequestAttributes"
  Add ThreadLocalAccessor for LocaleContext and RequestAttributes

Closes gh-32243
2024-02-15 15:58:06 +01:00
Stéphane Nicoll aef4b21f19 Polish "Add ThreadLocalAccessor for LocaleContext and RequestAttributes"
See gh-32243
2024-02-15 15:57:43 +01:00
Tadaya Tsuyukubo 5bd1c1fddb Add ThreadLocalAccessor for LocaleContext and RequestAttributes
Add `ThreadLocalAccessor` implementations:
- `LocaleThreadLocalAccessor`
- `RequestAttributesThreadLocalAccessor`

See gh-32243
2024-02-15 15:49:55 +01:00
Stéphane Nicoll 5d22aa9770 Add support for escaping prefix and separator in property placeholders
Closes gh-9628
Closes gh-26268
2024-02-15 15:28:36 +01:00
Stéphane Nicoll e3aa5b6b11 Use new implementation in PropertyPlaceholderHelper
This commit removes the previous implementation in favor of the new
PlaceholderParser. The only noticeable side effect is that the exception
is no longer an IllegalArgumentException, but rather the dedicated
PlaceholderResolutionException.

See gh-9628
2024-02-15 15:27:13 +01:00
Stéphane Nicoll 00e05e603d Add new property placeholder implementation
This commit provides a rewrite of the parser for properties containing
potentially placeholders.

Assuming a source where `firstName` = `John` and `lastName` = `Smith`,
the "${firstName}-${lastName}" property is evaluated as "John-Smith".

Compared with the existing implementation in PropertyPlaceholderHelper,
the new implementation offers the following extra features:

1. Placeholder can be escaped using a configurable escape character.
When a placeholder is escaped it is rendered as is. This does apply to
any nested placeholder that wouldn't be escaped. For instance,
"\${firstName}" is evaluated as "${firstName}".
2. The default separator can also be escaped the same way. When the
separator is escaped, the left and right parts are not considered as
the key and the default value respectively. Rather the two parts
combined, including the separator (but not the escape separator) are
used for resolution. For instance, ${java\:comp/env/test} is looking
for a "java:comp/env/test" property.
3. Placeholders are resolved lazily. Previously, all nested placeholders
were resolved before considering if a separator was present. This
implementation only attempts the resolution of the default value if the
key does not provide a value.
4. Failure to resolve a placeholder are more rich, with a dedicated
PlaceholderResolutionException that contains the resolution chain.

See gh-9628
See gh-26268
2024-02-15 15:27:13 +01:00
Stéphane Nicoll 0fbfecd392 Merge pull request #31513 from bclozel
* pr/31513:
  Support "--" end of options in SimpleCommandLineArgsParser

Closes gh-31513
2024-02-15 15:09:39 +01:00
Brian Clozel b4131ce131 Support "--" end of options in SimpleCommandLineArgsParser
Prior to this commit, the `SimpleCommandLineArgsParser` would reject
"--" arguments as invalid. As reported by the community, the POSIX
utility conventions (Guideline 10) state that

> The first -- argument that is not an option-argument should be
> accepted as a delimiter indicating the end of options.
> Any following arguments should be treated as operands, even if they
> begin with the '-' character.

This commit updates `SimpleCommandLineArgsParser` to not reject "--"
arguments and instead to consider remaining arguments as non-optional.

See gh-31513
2024-02-15 15:06:17 +01:00
Stéphane Nicoll 0f707706f1 Stop setting a thread name prefix for externally defined Executors
This commit updates the WebSocket message broker configuration to stop
setting a thread name prefix for externally defined Executors.

This used to apply to:

* clientInboundChannel with a thread name prefix of
"clientInboundChannel-".
* clientOutboundChannel with a thread name prefix of
"clientOutboundChannel-".
* brokerChannel with a thread name prefix of "brokerChannel-".

Closes gh-32132
2024-02-15 14:59:42 +01:00
Stéphane Nicoll f526b23fd7 Harmonize WebSocket message broker to use Executor
This commit harmonizes the configuration of the WebSocket message
broker to use Executor rather than TaskExecutor as only the former
is enforced. This lets custom configuration to use a wider range
of implementations.

Closes gh-32129
2024-02-15 14:59:42 +01:00
Stéphane Nicoll 2fc8b13dd5 Add support for MySQL backticks
This commit makes sure that content within backticks are skipped
when parsing a SQL statement using NamedParameterUtils. This harmonizes
the current behavior of ignoring special characters that are wrapped
in backticks.

Closes gh-31944
2024-02-15 14:41:30 +01:00
Stéphane Nicoll e73bbd4ad3 Configure jsonpath MappingProvider in WebTestClient
This commit improves jsonpath support in WebTestClient by detecting
a suitable json encoder/decoder that can be applied to assert more
complex data structure.

Closes gh-31653
2024-02-15 14:41:30 +01:00
Stéphane Nicoll 9f8038963f Allow JsonPathExpectationsHelper to use a custom configuration
This commit improves JsonPathExpectationsHelper to allow a custom json
path configuration to be defined. The primary objective of a custom
configuration is to specify a custom mapper that can deserialize complex
object structure.

As part of this commit, it is now possible to invoke a Matcher against
a type that holds generic information, using a regular
ParameterizedTypeReference.

Given that the existing constructor takes a vararg of Object, this
commit also deprecates this constructor in favor of formatting the
expression String upfront.

Closes gh-31651
2024-02-15 13:57:40 +01:00
Stéphane Nicoll 4f8eca1350 Order methods that take a type as a lambda consistently
Closes gh-32062
2024-02-15 13:57:17 +01:00
Stéphane Nicoll 18ea43c905 Allow customizations of embedded database connections
This commit allows EmbeddedDatabaseConfigurer instances to be further
customized if necessary. EmbeddedDatabaseBuilder has a way now to set
a DatabaseConfigurer rather than just a type to provide full control,
or customize an existing supported database type using the new
EmbeddedDatabaseConfigurers#customizeConfigurer callback.

Closes gh-21160
2024-02-15 13:46:49 +01:00
Stéphane Nicoll bcccba50c1 Merge branch '6.1.x' 2024-02-15 13:07:47 +01:00
Stéphane Nicoll 8288db496d Initiate Spring Framework 6.2.0 snapshots 2024-02-15 13:03:54 +01:00
1868 changed files with 51640 additions and 21267 deletions
@@ -1,20 +0,0 @@
name: Await HTTP Resource
description: 'Waits for an HTTP resource to be available (a HEAD request succeeds)'
inputs:
url:
description: 'URL of the resource to await'
required: true
runs:
using: composite
steps:
- name: Await HTTP resource
shell: bash
run: |
url=${{ inputs.url }}
echo "Waiting for $url"
until curl --fail --head --silent ${{ inputs.url }} > /dev/null
do
echo "."
sleep 60
done
echo "$url is available"
-61
View File
@@ -1,61 +0,0 @@
name: 'Build'
description: 'Builds the project, optionally publishing it to a local deployment repository'
inputs:
develocity-access-key:
description: 'Access key for authentication with ge.spring.io'
required: false
java-distribution:
description: 'Java distribution to use'
required: false
default: 'liberica'
java-early-access:
description: 'Whether the Java version is in early access'
required: false
default: 'false'
java-toolchain:
description: 'Whether a Java toolchain should be used'
required: false
default: 'false'
java-version:
description: 'Java version to compile and test with'
required: false
default: '17'
publish:
description: 'Whether to publish artifacts ready for deployment to Artifactory'
required: false
default: 'false'
outputs:
build-scan-url:
description: 'URL, if any, of the build scan produced by the build'
value: ${{ (inputs.publish == 'true' && steps.publish.outputs.build-scan-url) || steps.build.outputs.build-scan-url }}
version:
description: 'Version that was built'
value: ${{ steps.read-version.outputs.version }}
runs:
using: composite
steps:
- name: Prepare Gradle Build
uses: ./.github/actions/prepare-gradle-build
with:
develocity-access-key: ${{ inputs.develocity-access-key }}
java-distribution: ${{ inputs.java-distribution }}
java-early-access: ${{ inputs.java-early-access }}
java-toolchain: ${{ inputs.java-toolchain }}
java-version: ${{ inputs.java-version }}
- name: Build
id: build
if: ${{ inputs.publish == 'false' }}
shell: bash
run: ./gradlew check antora
- name: Publish
id: publish
if: ${{ inputs.publish == 'true' }}
shell: bash
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
- name: Read Version From gradle.properties
id: read-version
shell: bash
run: |
version=$(sed -n 's/version=\(.*\)/\1/p' gradle.properties)
echo "Version is $version"
echo "version=$version" >> $GITHUB_OUTPUT
@@ -1,23 +0,0 @@
name: Create GitHub Release
description: 'Create the release on GitHub with a changelog'
inputs:
milestone:
description: 'Name of the GitHub milestone for which a release will be created'
required: true
token:
description: 'Token to use for authentication with GitHub'
required: true
runs:
using: composite
steps:
- name: Generate Changelog
uses: spring-io/github-changelog-generator@185319ad7eaa75b0e8e72e4b6db19c8b2cb8c4c1 #v0.0.11
with:
config-file: .github/actions/create-github-release/changelog-generator.yml
milestone: ${{ inputs.milestone }}
token: ${{ inputs.token }}
- name: Create GitHub Release
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.token }}
run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md
@@ -1,53 +0,0 @@
name: Prepare Gradle Build
description: 'Prepares a Gradle build. Sets up Java and Gradle and configures Gradle properties'
inputs:
develocity-access-key:
description: 'Access key for authentication with ge.spring.io'
required: false
java-distribution:
description: 'Java distribution to use'
required: false
default: 'liberica'
java-early-access:
description: 'Whether the Java version is in early access. When true, forces java-distribution to temurin'
required: false
default: 'false'
java-toolchain:
description: 'Whether a Java toolchain should be used'
required: false
default: 'false'
java-version:
description: 'Java version to use for the build'
required: false
default: '17'
runs:
using: composite
steps:
- name: Set Up Java
uses: actions/setup-java@v4
with:
distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || (inputs.java-distribution || 'liberica') }}
java-version: |
${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }}
${{ inputs.java-toolchain == 'true' && '17' || '' }}
- name: Set Up Gradle
uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
with:
cache-read-only: false
develocity-access-key: ${{ inputs.develocity-access-key }}
- name: Configure Gradle Properties
shell: bash
run: |
mkdir -p $HOME/.gradle
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
- name: Configure Toolchain Properties
if: ${{ inputs.java-toolchain == 'true' }}
shell: bash
run: |
echo toolchainVersion=${{ inputs.java-version }} >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.auto-detect=false >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.auto-download=false >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', inputs.java-version) }} >> $HOME/.gradle/gradle.properties
@@ -1,5 +1,5 @@
name: Print JVM thread dumps
description: 'Prints a thread dump for all running JVMs'
description: Prints a thread dump for all running JVMs
runs:
using: composite
steps:
@@ -7,7 +7,7 @@ runs:
shell: bash
run: |
for jvm_pid in $(jps -q -J-XX:+PerfDisableSharedMem); do
jcmd $jvm_pid Thread.print
jcmd $java_pid Thread.print
done
- if: ${{ runner.os == 'Windows' }}
shell: powershell
+14 -20
View File
@@ -1,39 +1,33 @@
name: Send Notification
description: 'Sends a Google Chat message as a notification of the job''s outcome'
name: Send notification
description: Sends a Google Chat message as a notification of the job's outcome
inputs:
build-scan-url:
description: 'URL of the build scan to include in the notification'
required: false
run-name:
description: 'Name of the run to include in the notification'
required: false
default: ${{ format('{0} {1}', github.ref_name, github.job) }}
status:
description: 'Status of the job'
required: true
webhook-url:
description: 'Google Chat Webhook URL'
required: true
status:
description: 'Status of the job'
required: true
build-scan-url:
description: 'URL of the build scan to include in the notification'
run-name:
description: 'Name of the run to include in the notification'
default: ${{ format('{0} {1}', github.ref_name, github.job) }}
runs:
using: composite
steps:
- name: Prepare Variables
shell: bash
- shell: bash
run: |
echo "BUILD_SCAN=${{ inputs.build-scan-url == '' && ' [build scan unavailable]' || format(' [<{0}|Build Scan>]', inputs.build-scan-url) }}" >> "$GITHUB_ENV"
echo "RUN_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$GITHUB_ENV"
- name: Success Notification
- shell: bash
if: ${{ inputs.status == 'success' }}
shell: bash
run: |
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was successful ${{ env.BUILD_SCAN }}"}' || true
- name: Failure Notification
- shell: bash
if: ${{ inputs.status == 'failure' }}
shell: bash
run: |
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<users/all> *<${{ env.RUN_URL }}|${{ inputs.run-name }}> failed* ${{ env.BUILD_SCAN }}"}' || true
- name: Cancel Notification
- shell: bash
if: ${{ inputs.status == 'cancelled' }}
shell: bash
run: |
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true
@@ -1,43 +0,0 @@
name: Sync to Maven Central
description: 'Syncs a release to Maven Central and waits for it to be available for use'
inputs:
jfrog-cli-config-token:
description: 'Config token for the JFrog CLI'
required: true
ossrh-s01-staging-profile:
description: 'Staging profile to use when syncing to Central'
required: true
ossrh-s01-token-password:
description: 'Password for authentication with s01.oss.sonatype.org'
required: true
ossrh-s01-token-username:
description: 'Username for authentication with s01.oss.sonatype.org'
required: true
spring-framework-version:
description: 'Version of Spring Framework that is being synced to Central'
required: true
runs:
using: composite
steps:
- name: Set Up JFrog CLI
uses: jfrog/setup-jfrog-cli@9fe0f98bd45b19e6e931d457f4e98f8f84461fb5 # v4.4.1
env:
JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }}
- name: Download Release Artifacts
shell: bash
run: jf rt download --spec ${{ format('{0}/artifacts.spec', github.action_path) }} --spec-vars 'buildName=${{ format('spring-framework-{0}', inputs.spring-framework-version) }};buildNumber=${{ github.run_number }}'
- name: Sync
uses: spring-io/nexus-sync-action@42477a2230a2f694f9eaa4643fa9e76b99b7ab84 # v0.0.1
with:
close: true
create: true
generate-checksums: true
password: ${{ inputs.ossrh-s01-token-password }}
release: true
staging-profile-name: ${{ inputs.ossrh-s01-staging-profile }}
upload: true
username: ${{ inputs.ossrh-s01-token-username }}
- name: Await
uses: ./.github/actions/await-http-resource
with:
url: ${{ format('https://repo.maven.apache.org/maven2/org/springframework/spring-context/{0}/spring-context-{0}.jar', inputs.spring-framework-version) }}
@@ -1,20 +0,0 @@
{
"files": [
{
"aql": {
"items.find": {
"$and": [
{
"@build.name": "${buildName}",
"@build.number": "${buildNumber}",
"path": {
"$nmatch": "org/springframework/framework-api/*"
}
}
]
}
},
"target": "nexus/"
}
]
}
+2 -1
View File
@@ -1,4 +1,5 @@
name: Backport Bot
on:
issues:
types: [labeled]
@@ -28,6 +29,6 @@ jobs:
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
- name: Backport
env:
GITHUB_EVENT: ${{ toJSON(github.event) }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_EVENT: ${{ toJSON(github.event) }}
run: java -jar backport-bot-0.0.1-SNAPSHOT.jar --github.accessToken="$GITHUB_TOKEN" --github.event_name "$GITHUB_EVENT_NAME" --github.event "$GITHUB_EVENT"
+41 -37
View File
@@ -1,58 +1,62 @@
name: Build and Deploy Snapshot
name: Build and deploy snapshot
on:
push:
branches:
- 6.1.x
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
build-and-deploy-snapshot:
name: Build and Deploy Snapshot
if: ${{ github.repository == 'spring-projects/spring-framework' }}
name: Build and deploy snapshot
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Check Out Code
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'liberica'
java-version: 17
- name: Check out code
uses: actions/checkout@v4
- name: Build and Publish
id: build-and-publish
uses: ./.github/actions/build
- name: Set up Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
with:
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
publish: true
cache-read-only: false
- name: Configure Gradle properties
shell: bash
run: |
mkdir -p $HOME/.gradle
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
- name: Build and publish
id: build
env:
CI: 'true'
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
- name: Deploy
uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1
uses: spring-io/artifactory-deploy-action@v0.0.1
with:
uri: 'https://repo.spring.io'
username: ${{ secrets.ARTIFACTORY_USERNAME }}
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
build-name: ${{ format('spring-framework-{0}', github.ref_name)}}
repository: 'libs-snapshot-local'
folder: 'deployment-repository'
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
artifact-properties: |
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
/**/framework-api-*-docs.zip::zip.type=docs
/**/framework-api-*-schema.zip::zip.type=schema
build-name: 'spring-framework-6.1.x'
folder: 'deployment-repository'
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
repository: 'libs-snapshot-local'
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
uri: 'https://repo.spring.io'
username: ${{ secrets.ARTIFACTORY_USERNAME }}
- name: Send Notification
if: always()
- name: Send notification
uses: ./.github/actions/send-notification
if: always()
with:
build-scan-url: ${{ steps.build-and-publish.outputs.build-scan-url }}
run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }}
status: ${{ job.status }}
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
outputs:
version: ${{ steps.build-and-publish.outputs.version }}
verify:
name: Verify
needs: build-and-deploy-snapshot
uses: ./.github/workflows/verify.yml
secrets:
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
with:
version: ${{ needs.build-and-deploy-snapshot.outputs.version }}
status: ${{ job.status }}
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }}
+21 -14
View File
@@ -1,36 +1,43 @@
name: Build Pull Request
on: pull_request
permissions:
contents: read
jobs:
build:
name: Build Pull Request
if: ${{ github.repository == 'spring-projects/spring-framework' }}
name: Build pull request
runs-on: ubuntu-latest
timeout-minutes: 60
if: ${{ github.repository == 'spring-projects/spring-framework' }}
steps:
- name: Set Up JDK 17
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'liberica'
java-version: '17'
- name: Check Out
distribution: 'liberica'
- name: Check out code
uses: actions/checkout@v4
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
- name: Set Up Gradle
uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2
- name: Set up Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
- name: Build
env:
CI: 'true'
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
run: ./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --no-parallel --continue build
- name: Print JVM Thread Dumps When Cancelled
if: cancelled()
- name: Print JVM thread dumps when cancelled
uses: ./.github/actions/print-jvm-thread-dumps
- name: Upload Build Reports
if: failure()
if: cancelled()
- name: Upload build reports
uses: actions/upload-artifact@v4
if: failure()
with:
name: build-reports
path: '**/build/reports/'
+42 -23
View File
@@ -1,16 +1,12 @@
name: CI
on:
push:
branches:
- 6.1.x
schedule:
- cron: '30 9 * * *'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
ci:
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
if: ${{ github.repository == 'spring-projects/spring-framework' }}
runs-on: ${{ matrix.os.id }}
timeout-minutes: 60
strategy:
matrix:
os:
@@ -21,38 +17,61 @@ jobs:
toolchain: false
- version: 21
toolchain: true
- version: 22
toolchain: true
- version: 23
toolchain: true
exclude:
- os:
name: Linux
java:
version: 17
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
runs-on: ${{ matrix.os.id }}
steps:
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'liberica'
java-version: |
${{ matrix.java.version }}
${{ matrix.java.toolchain && '17' || '' }}
- name: Prepare Windows runner
if: ${{ runner.os == 'Windows' }}
run: |
git config --global core.autocrlf true
git config --global core.longPaths true
Stop-Service -name Docker
- name: Check Out Code
- name: Check out code
uses: actions/checkout@v4
- name: Set up Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
with:
cache-read-only: false
- name: Configure Gradle properties
shell: bash
run: |
mkdir -p $HOME/.gradle
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
- name: Configure toolchain properties
if: ${{ matrix.java.toolchain }}
shell: bash
run: |
echo toolchainVersion=${{ matrix.java.version }} >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.auto-detect=false >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.auto-download=false >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', matrix.java.version) }} >> $HOME/.gradle/gradle.properties
- name: Build
id: build
uses: ./.github/actions/build
with:
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
java-early-access: ${{ matrix.java.early-access || 'false' }}
java-distribution: ${{ matrix.java.distribution }}
java-toolchain: ${{ matrix.java.toolchain }}
java-version: ${{ matrix.java.version }}
- name: Send Notification
if: always()
env:
CI: 'true'
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
run: ./gradlew check antora
- name: Send notification
uses: ./.github/actions/send-notification
if: always()
with:
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
run-name: ${{ format('{0} | {1} | Java {2}', github.ref_name, matrix.os.name, matrix.java.version) }}
status: ${{ job.status }}
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
status: ${{ job.status }}
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
run-name: ${{ format('{0} | {1} | Java {2}', github.ref_name, matrix.os.name, matrix.java.version) }}
+2 -3
View File
@@ -14,15 +14,14 @@ permissions:
actions: write
jobs:
build:
name: Dispatch docs deployment
if: github.repository_owner == 'spring-projects'
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects'
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 1
ref: docs-build
fetch-depth: 1
- name: Dispatch (partial build)
if: github.ref_type == 'branch'
env:
@@ -0,0 +1,13 @@
name: "Validate Gradle Wrapper"
on: [push, pull_request]
permissions:
contents: read
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v2
-94
View File
@@ -1,94 +0,0 @@
name: Release
on:
push:
tags:
- v6.1.[0-9]+
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
build-and-stage-release:
name: Build and Stage Release
if: ${{ github.repository == 'spring-projects/spring-framework' }}
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v4
- name: Build and Publish
id: build-and-publish
uses: ./.github/actions/build
with:
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
publish: true
- name: Stage Release
uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1
with:
artifact-properties: |
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
/**/framework-api-*-docs.zip::zip.type=docs
/**/framework-api-*-schema.zip::zip.type=schema
build-name: ${{ format('spring-framework-{0}', steps.build-and-publish.outputs.version)}}
folder: 'deployment-repository'
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
repository: 'libs-staging-local'
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
uri: 'https://repo.spring.io'
username: ${{ secrets.ARTIFACTORY_USERNAME }}
outputs:
version: ${{ steps.build-and-publish.outputs.version }}
verify:
name: Verify
needs: build-and-stage-release
uses: ./.github/workflows/verify.yml
secrets:
google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }}
repository-username: ${{ secrets.ARTIFACTORY_USERNAME }}
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
with:
staging: true
version: ${{ needs.build-and-stage-release.outputs.version }}
sync-to-maven-central:
name: Sync to Maven Central
needs:
- build-and-stage-release
- verify
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Sync to Maven Central
uses: ./.github/actions/sync-to-maven-central
with:
jfrog-cli-config-token: ${{ secrets.JF_ARTIFACTORY_SPRING }}
ossrh-s01-staging-profile: ${{ secrets.OSSRH_S01_STAGING_PROFILE }}
ossrh-s01-token-password: ${{ secrets.OSSRH_S01_TOKEN_PASSWORD }}
ossrh-s01-token-username: ${{ secrets.OSSRH_S01_TOKEN_USERNAME }}
spring-framework-version: ${{ needs.build-and-stage-release.outputs.version }}
promote-release:
name: Promote Release
needs:
- build-and-stage-release
- sync-to-maven-central
runs-on: ubuntu-latest
steps:
- name: Set up JFrog CLI
uses: jfrog/setup-jfrog-cli@9fe0f98bd45b19e6e931d457f4e98f8f84461fb5 # v4.4.1
env:
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
- name: Promote build
run: jfrog rt build-promote ${{ format('spring-framework-{0}', needs.build-and-stage-release.outputs.version)}} ${{ github.run_number }} libs-release-local
create-github-release:
name: Create GitHub Release
needs:
- build-and-stage-release
- promote-release
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Create GitHub Release
uses: ./.github/actions/create-github-release
with:
milestone: ${{ needs.build-and-stage-release.outputs.version }}
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
-77
View File
@@ -1,77 +0,0 @@
name: Verify
on:
workflow_call:
inputs:
staging:
description: 'Whether the release to verify is in the staging repository'
required: false
default: false
type: boolean
version:
description: 'Version to verify'
required: true
type: string
secrets:
google-chat-webhook-url:
description: 'Google Chat Webhook URL'
required: true
repository-password:
description: 'Password for authentication with the repository'
required: false
repository-username:
description: 'Username for authentication with the repository'
required: false
token:
description: 'Token to use for authentication with GitHub'
required: true
jobs:
verify:
name: Verify
runs-on: ubuntu-latest
steps:
- name: Check Out Release Verification Tests
uses: actions/checkout@v4
with:
ref: 'v0.0.2'
repository: spring-projects/spring-framework-release-verification
token: ${{ secrets.token }}
- name: Check Out Send Notification Action
uses: actions/checkout@v4
with:
path: send-notification
sparse-checkout: .github/actions/send-notification
- name: Set Up Java
uses: actions/setup-java@v4
with:
distribution: 'liberica'
java-version: 17
- name: Set Up Gradle
uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
with:
cache-read-only: false
- name: Configure Gradle Properties
shell: bash
run: |
mkdir -p $HOME/.gradle
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
- name: Run Release Verification Tests
env:
RVT_OSS_REPOSITORY_PASSWORD: ${{ secrets.repository-password }}
RVT_OSS_REPOSITORY_USERNAME: ${{ secrets.repository-username }}
RVT_RELEASE_TYPE: oss
RVT_STAGING: ${{ inputs.staging }}
RVT_VERSION: ${{ inputs.version }}
run: ./gradlew spring-framework-release-verification-tests:test
- name: Upload Build Reports on Failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: build-reports
path: '**/build/reports/'
- name: Send Notification
if: failure()
uses: ./send-notification/.github/actions/send-notification
with:
run-name: ${{ format('{0} | Verification | {1}', github.ref_name, inputs.version) }}
status: ${{ job.status }}
webhook-url: ${{ secrets.google-chat-webhook-url }}
-2
View File
@@ -51,5 +51,3 @@ atlassian-ide-plugin.xml
.vscode/
cached-antora-playbook.yml
node_modules
+1 -1
View File
@@ -1,3 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=17.0.12-librca
java=17.0.11-librca
+44
View File
@@ -0,0 +1,44 @@
= Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open
and welcoming community, we pledge to respect all people who contribute through reporting
issues, posting feature requests, updating documentation, submitting pull requests or
patches, and other activities.
We are committed to making participation in this project a harassment-free experience for
everyone, regardless of level of experience, gender, gender identity and expression,
sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses,
without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or reject comments,
commits, code, wiki edits, issues, and other contributions that are not aligned to this
Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors
that they deem inappropriate, threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to fairly and
consistently applying these principles to every aspect of managing this project. Project
maintainers who do not follow or enforce the Code of Conduct may be permanently removed
from the project team.
This Code of Conduct applies both within project spaces and in public spaces when an
individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will
be reviewed and investigated and will result in a response that is deemed necessary and
appropriate to the circumstances. Maintainers are obligated to maintain confidentiality
with regard to the reporter of an incident.
This Code of Conduct is adapted from the
https://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at
https://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/]
+1 -1
View File
@@ -18,7 +18,7 @@ First off, thank you for taking the time to contribute! :+1: :tada:
This project is governed by the [Spring Code of Conduct](CODE_OF_CONDUCT.adoc).
By participating you are expected to uphold this code.
Please report unacceptable behavior to spring-code-of-conduct@spring.io.
Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
### How to Contribute
+2 -2
View File
@@ -1,4 +1,4 @@
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml/badge.svg?branch=6.1.x)](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3A6.1.x) [![Revved up by Develocity](https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml/badge.svg?branch=main)](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3Amain) [![Revved up by Develocity](https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
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".
@@ -6,7 +6,7 @@ Spring provides everything required beyond the Java programming language for cre
## Code of Conduct
This project is governed by the [Spring Code of Conduct](CODE_OF_CONDUCT.adoc). By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to spring-code-of-conduct@spring.io.
This project is governed by the [Spring Code of Conduct](CODE_OF_CONDUCT.adoc). By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
## Access to Binaries
+8 -5
View File
@@ -2,13 +2,14 @@ plugins {
id 'io.freefair.aspectj' version '8.4' 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.20'
id 'org.jetbrains.dokka' version '1.9.20'
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
id 'com.github.ben-manes.versions' version '0.51.0'
id 'com.github.bjornvester.xjc' version '1.8.2' apply false
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
id 'de.undercouch.download' version '5.4.0'
id 'io.github.goooler.shadow' version '8.1.8' apply false
id 'me.champeau.jmh' version '0.7.2' apply false
id 'me.champeau.mrjar' version '0.1.1'
id "net.ltgt.errorprone" version "3.1.0" apply false
}
ext {
@@ -89,19 +90,21 @@ configure([rootProject] + javaProjects) { project ->
ext.javadocLinks = [
"https://docs.oracle.com/en/java/javase/17/docs/api/",
"https://jakarta.ee/specifications/platform/9/apidocs/",
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ and weblogic.* packages
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/", // org.jboss.resource.*
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
"https://eclipse.dev/aspectj/doc/released/aspectj5rt-api",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://fasterxml.github.io/jackson-core/javadoc/2.14/",
"https://fasterxml.github.io/jackson-databind/javadoc/2.14/",
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.14/",
"https://hc.apache.org/httpcomponents-client-5.4.x/current/httpclient5/apidocs/",
"https://hc.apache.org/httpcomponents-client-5.2.x/current/httpclient5/apidocs/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
// TODO Uncomment link to JUnit 5 docs once we execute Gradle with Java 18+.
// See https://github.com/spring-projects/spring-framework/issues/27497
//
// "https://junit.org/junit5/docs/5.10.4/api/",
// "https://junit.org/junit5/docs/5.10.2/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/",
+1 -1
View File
@@ -1,2 +1,2 @@
org.gradle.caching=true
javaFormatVersion=0.0.42
javaFormatVersion=0.0.41
@@ -50,7 +50,7 @@ public class CheckstyleConventions {
project.getPlugins().apply(CheckstylePlugin.class);
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
checkstyle.setToolVersion("10.20.1");
checkstyle.setToolVersion("10.16.0");
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
@@ -64,7 +64,7 @@ public class CheckstyleConventions {
NoHttpExtension noHttp = project.getExtensions().getByType(NoHttpExtension.class);
noHttp.setAllowlistFile(project.file("src/nohttp/allowlist.lines"));
noHttp.getSource().exclude("**/test-output/**", "**/.settings/**",
"**/.classpath", "**/.project", "**/.gradle/**", "**/node_modules/**");
"**/.classpath", "**/.project", "**/.gradle/**");
List<String> buildFolders = List.of("bin", "build", "out");
project.allprojects(subproject -> {
Path rootPath = project.getRootDir().toPath();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 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.
@@ -62,11 +62,9 @@ class TestConventions {
if (project.hasProperty("testGroups")) {
test.systemProperty("testGroups", project.getProperties().get("testGroups"));
}
test.jvmArgs(
"--add-opens=java.base/java.lang=ALL-UNNAMED",
test.jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED",
"--add-opens=java.base/java.util=ALL-UNNAMED",
"-Xshare:off"
);
"-Djava.locale.providers=COMPAT");
}
private void configureTestRetryPlugin(Project project, Test test) {
@@ -26,9 +26,7 @@ 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.plugins.jvm.JvmTestSuite;
import org.gradle.api.tasks.testing.Test;
import org.gradle.testing.base.TestingExtension;
import java.util.Collections;
@@ -49,8 +47,6 @@ public class RuntimeHintsAgentPlugin implements Plugin<Project> {
public void apply(Project project) {
project.getPlugins().withType(JavaPlugin.class, javaPlugin -> {
TestingExtension testing = project.getExtensions().getByType(TestingExtension.class);
JvmTestSuite jvmTestSuite = (JvmTestSuite) testing.getSuites().getByName("test");
RuntimeHintsAgentExtension agentExtension = createRuntimeHintsAgentExtension(project);
Test agentTest = project.getTasks().create(RUNTIMEHINTS_TEST_TASK, Test.class, test -> {
test.useJUnitPlatform(options -> {
@@ -59,8 +55,6 @@ public class RuntimeHintsAgentPlugin implements Plugin<Project> {
test.include("**/*Tests.class", "**/*Test.class");
test.systemProperty("java.awt.headless", "true");
test.systemProperty("org.graalvm.nativeimage.imagecode", "runtime");
test.setTestClassesDirs(jvmTestSuite.getSources().getOutput().getClassesDirs());
test.setClasspath(jvmTestSuite.getSources().getRuntimeClasspath());
test.getJvmArgumentProviders().add(createRuntimeHintsAgentArgumentProvider(project, agentExtension));
});
project.getTasks().getByName("check", task -> task.dependsOn(agentTest));
+59
View File
@@ -0,0 +1,59 @@
== Spring Framework Concourse pipeline
NOTE: CI is being migrated to GitHub Actions.
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.2.x[Spring Framework 6.2.x].
=== Setting up your development environment
If you're part of the Spring Framework project on GitHub, you can get access to CI management features.
First, you need to go to https://ci.spring.io and install the client CLI for your platform (see bottom right of the screen).
You can then login with the instance using:
[source]
----
$ fly -t spring login -n spring-framework -c https://ci.spring.io
----
Once logged in, you should get something like:
[source]
----
$ fly ts
name url team expiry
spring https://ci.spring.io spring-framework Wed, 25 Mar 2020 17:45:26 UTC
----
=== Pipeline configuration and structure
The build pipelines are described in `pipeline.yml` file.
This file is listing Concourse resources, i.e. build inputs and outputs such as container images, artifact repositories, source repositories, notification services, etc.
It also describes jobs (a job is a sequence of inputs, tasks and outputs); jobs are organized by groups.
The `pipeline.yml` definition contains `((parameters))` which are loaded from the `parameters.yml` file or from our https://docs.cloudfoundry.org/credhub/[credhub instance].
You'll find in this folder the following resources:
* `pipeline.yml` the build pipeline
* `parameters.yml` the build parameters used for the pipeline
* `images/` holds the container images definitions used in this pipeline
* `scripts/` holds the build scripts that ship within the CI container images
* `tasks` contains the task definitions used in the main `pipeline.yml`
=== Updating the build pipeline
Updating files on the repository is not enough to update the build pipeline, as changes need to be applied.
The pipeline can be deployed using the following command:
[source]
----
$ fly -t spring set-pipeline -p spring-framework-6.2.x -c ci/pipeline.yml -l ci/parameters.yml
----
NOTE: This assumes that you have credhub integration configured with the appropriate secrets.
@@ -17,12 +17,4 @@ changelog:
- "type: dependency-upgrade"
contributors:
exclude:
names:
- "bclozel"
- "jhoeller"
- "poutsma"
- "rstoyanchev"
- "sbrannen"
- "sdeleuze"
- "simonbasle"
- "snicoll"
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll", "simonbasle"]
+10
View File
@@ -0,0 +1,10 @@
logging:
level:
io.spring.concourse: DEBUG
spring:
main:
banner-mode: off
sonatype:
exclude:
- 'build-info\.json'
- '.*\.zip'
+21
View File
@@ -0,0 +1,21 @@
== CI Images
These images are used by CI to run the actual builds.
To build the image locally run the following from this directory:
----
$ docker build --no-cache -f <image-folder>/Dockerfile .
----
For example
----
$ docker build --no-cache -f spring-framework-ci-image/Dockerfile .
----
To test run:
----
$ docker run -it --entrypoint /bin/bash <SHA>
----
+12
View File
@@ -0,0 +1,12 @@
FROM ubuntu:jammy-20240125
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
RUN ./setup.sh
ENV JAVA_HOME /opt/openjdk/java17
ENV JDK17 /opt/openjdk/java17
ENV JDK21 /opt/openjdk/java21
ENV JDK23 /opt/openjdk/java23
ENV PATH $JAVA_HOME/bin:$PATH
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
set -e
case "$1" in
java17)
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.10%2B13/bellsoft-jdk17.0.10+13-linux-amd64.tar.gz"
;;
java21)
echo "https://github.com/bell-sw/Liberica/releases/download/21.0.2%2B14/bellsoft-jdk21.0.2+14-linux-amd64.tar.gz"
;;
java23)
echo "https://download.java.net/java/early_access/jdk23/17/GPL/openjdk-23-ea+17_linux-x64_bin.tar.gz"
;;
*)
echo $"Unknown java version"
exit 1
esac
+40
View File
@@ -0,0 +1,40 @@
#!/bin/bash
set -ex
###########################################################
# UTILS
###########################################################
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install --no-install-recommends -y tzdata ca-certificates net-tools libxml2-utils git curl libudev1 libxml2-utils iptables iproute2 jq fontconfig
ln -fs /usr/share/zoneinfo/UTC /etc/localtime
dpkg-reconfigure --frontend noninteractive tzdata
rm -rf /var/lib/apt/lists/*
curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/concourse-java.sh > /opt/concourse-java.sh
###########################################################
# JAVA
###########################################################
mkdir -p /opt/openjdk
pushd /opt/openjdk > /dev/null
for jdk in java17 java21 java23
do
JDK_URL=$( /get-jdk-url.sh $jdk )
mkdir $jdk
pushd $jdk > /dev/null
curl -L ${JDK_URL} | tar zx --strip-components=1
test -f bin/java
test -f bin/javac
popd > /dev/null
done
popd
###########################################################
# GRADLE ENTERPRISE
###########################################################
cd /
mkdir ~/.gradle
echo 'systemProp.user.name=concourse' > ~/.gradle/gradle.properties
+10
View File
@@ -0,0 +1,10 @@
github-repo: "https://github.com/spring-projects/spring-framework.git"
github-repo-name: "spring-projects/spring-framework"
sonatype-staging-profile: "org.springframework"
docker-hub-organization: "springci"
artifactory-server: "https://repo.spring.io"
branch: "main"
milestone: "6.2.x"
build-name: "spring-framework"
pipeline-name: "spring-framework"
concourse-url: "https://ci.spring.io"
+293
View File
@@ -0,0 +1,293 @@
anchors:
git-repo-resource-source: &git-repo-resource-source
uri: ((github-repo))
username: ((github-username))
password: ((github-ci-release-token))
branch: ((branch))
gradle-enterprise-task-params: &gradle-enterprise-task-params
DEVELOCITY_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
sonatype-task-params: &sonatype-task-params
SONATYPE_USERNAME: ((sonatype-username))
SONATYPE_PASSWORD: ((sonatype-password))
SONATYPE_URL: ((sonatype-url))
SONATYPE_STAGING_PROFILE: ((sonatype-staging-profile))
artifactory-task-params: &artifactory-task-params
ARTIFACTORY_SERVER: ((artifactory-server))
ARTIFACTORY_USERNAME: ((artifactory-username))
ARTIFACTORY_PASSWORD: ((artifactory-password))
build-project-task-params: &build-project-task-params
BRANCH: ((branch))
<<: *gradle-enterprise-task-params
docker-resource-source: &docker-resource-source
username: ((docker-hub-username))
password: ((docker-hub-password))
changelog-task-params: &changelog-task-params
name: generated-changelog/tag
tag: generated-changelog/tag
body: generated-changelog/changelog.md
github-task-params: &github-task-params
GITHUB_USERNAME: ((github-username))
GITHUB_TOKEN: ((github-ci-release-token))
resource_types:
- name: registry-image
type: registry-image
source:
<<: *docker-resource-source
repository: concourse/registry-image-resource
tag: 1.8.0
- name: artifactory-resource
type: registry-image
source:
<<: *docker-resource-source
repository: springio/artifactory-resource
tag: 0.0.18
- name: github-release
type: registry-image
source:
<<: *docker-resource-source
repository: concourse/github-release-resource
tag: 1.8.0
- name: github-status-resource
type: registry-image
source:
<<: *docker-resource-source
repository: dpb587/github-status-resource
tag: master
resources:
- name: git-repo
type: git
icon: github
source:
<<: *git-repo-resource-source
- name: ci-images-git-repo
type: git
icon: github
source:
uri: ((github-repo))
branch: ((branch))
paths: ["ci/images/*"]
- name: ci-image
type: registry-image
icon: docker
source:
<<: *docker-resource-source
repository: ((docker-hub-organization))/spring-framework-ci
tag: ((milestone))
- name: artifactory-repo
type: artifactory-resource
icon: package-variant
source:
uri: ((artifactory-server))
username: ((artifactory-username))
password: ((artifactory-password))
build_name: ((build-name))
- name: github-pre-release
type: github-release
icon: briefcase-download-outline
source:
owner: spring-projects
repository: spring-framework
access_token: ((github-ci-release-token))
pre_release: true
release: false
- name: github-release
type: github-release
icon: briefcase-download
source:
owner: spring-projects
repository: spring-framework
access_token: ((github-ci-release-token))
pre_release: false
jobs:
- name: build-ci-images
plan:
- get: git-repo
- get: ci-images-git-repo
trigger: true
- task: build-ci-image
privileged: true
file: git-repo/ci/tasks/build-ci-image.yml
output_mapping:
image: ci-image
vars:
ci-image-name: ci-image
<<: *docker-resource-source
- put: ci-image
params:
image: ci-image/image.tar
- name: stage-milestone
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: M
<<: *gradle-enterprise-task-params
- put: artifactory-repo
params: &artifactory-params
signing_key: ((signing-key))
signing_passphrase: ((signing-passphrase))
repo: libs-staging-local
folder: distribution-repository
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
disable_checksum_uploads: true
threads: 8
artifact_set:
- include:
- "/**/framework-api-*.zip"
properties:
"zip.name": "spring-framework"
"zip.displayname": "Spring Framework"
"zip.deployed": "false"
- include:
- "/**/framework-api-*-docs.zip"
properties:
"zip.type": "docs"
- include:
- "/**/framework-api-*-schema.zip"
properties:
"zip.type": "schema"
get_params:
threads: 8
- put: git-repo
params:
repository: stage-git-repo
- name: promote-milestone
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- get: artifactory-repo
trigger: false
passed: [stage-milestone]
params:
download_artifacts: false
save_build_info: true
- task: promote
file: git-repo/ci/tasks/promote-version.yml
params:
RELEASE_TYPE: M
<<: *artifactory-task-params
- task: generate-changelog
file: git-repo/ci/tasks/generate-changelog.yml
params:
RELEASE_TYPE: M
<<: *github-task-params
<<: *docker-resource-source
- put: github-pre-release
params:
<<: *changelog-task-params
- name: stage-rc
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: RC
<<: *gradle-enterprise-task-params
- put: artifactory-repo
params:
<<: *artifactory-params
- put: git-repo
params:
repository: stage-git-repo
- name: promote-rc
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- get: artifactory-repo
trigger: false
passed: [stage-rc]
params:
download_artifacts: false
save_build_info: true
- task: promote
file: git-repo/ci/tasks/promote-version.yml
params:
RELEASE_TYPE: RC
<<: *docker-resource-source
<<: *artifactory-task-params
- task: generate-changelog
file: git-repo/ci/tasks/generate-changelog.yml
params:
RELEASE_TYPE: RC
<<: *github-task-params
- put: github-pre-release
params:
<<: *changelog-task-params
- name: stage-release
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: RELEASE
<<: *gradle-enterprise-task-params
- put: artifactory-repo
params:
<<: *artifactory-params
- put: git-repo
params:
repository: stage-git-repo
- name: promote-release
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- get: artifactory-repo
trigger: false
passed: [stage-release]
params:
download_artifacts: true
save_build_info: true
- task: promote
file: git-repo/ci/tasks/promote-version.yml
params:
RELEASE_TYPE: RELEASE
<<: *docker-resource-source
<<: *artifactory-task-params
<<: *sonatype-task-params
- name: create-github-release
serial: true
plan:
- get: ci-image
- get: git-repo
- get: artifactory-repo
trigger: true
passed: [promote-release]
params:
download_artifacts: false
save_build_info: true
- task: generate-changelog
file: git-repo/ci/tasks/generate-changelog.yml
params:
RELEASE_TYPE: RELEASE
<<: *docker-resource-source
<<: *github-task-params
- put: github-release
params:
<<: *changelog-task-params
groups:
- name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
jobs: ["build-ci-images"]
+2
View File
@@ -0,0 +1,2 @@
source /opt/concourse-java.sh
setup_symlinks
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
set -e
CONFIG_DIR=git-repo/ci/config
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
java -jar /github-changelog-generator.jar \
--spring.config.location=${CONFIG_DIR}/changelog-generator.yml \
${version} generated-changelog/changelog.md
echo ${version} > generated-changelog/version
echo v${version} > generated-changelog/tag
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
CONFIG_DIR=git-repo/ci/config
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
export BUILD_INFO_LOCATION=$(pwd)/artifactory-repo/build-info.json
java -jar /concourse-release-scripts.jar \
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
publishToCentral $RELEASE_TYPE $BUILD_INFO_LOCATION artifactory-repo || { exit 1; }
java -jar /concourse-release-scripts.jar \
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
promote $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
echo "Promotion complete"
echo $version > version/version
+51
View File
@@ -0,0 +1,51 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
repository=$(pwd)/distribution-repository
pushd git-repo > /dev/null
git fetch --tags --all > /dev/null
popd > /dev/null
git clone git-repo stage-git-repo > /dev/null
pushd stage-git-repo > /dev/null
snapshotVersion=$( awk -F '=' '$1 == "version" { print $2 }' gradle.properties )
if [[ $RELEASE_TYPE = "M" ]]; then
stageVersion=$( get_next_milestone_release $snapshotVersion)
nextVersion=$snapshotVersion
elif [[ $RELEASE_TYPE = "RC" ]]; then
stageVersion=$( get_next_rc_release $snapshotVersion)
nextVersion=$snapshotVersion
elif [[ $RELEASE_TYPE = "RELEASE" ]]; then
stageVersion=$( get_next_release $snapshotVersion)
nextVersion=$( bump_version_number $snapshotVersion)
else
echo "Unknown release type $RELEASE_TYPE" >&2; exit 1;
fi
echo "Staging $stageVersion (next version will be $nextVersion)"
sed -i "s/version=$snapshotVersion/version=$stageVersion/" gradle.properties
git config user.name "Spring Builds" > /dev/null
git config user.email "spring-builds@users.noreply.github.com" > /dev/null
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} -Porg.gradle.java.installations.fromEnv=JDK17,JDK21 \
build publishAllPublicationsToDeploymentRepository
git reset --hard HEAD^ > /dev/null
if [[ $nextVersion != $snapshotVersion ]]; then
echo "Setting next development version (v$nextVersion)"
sed -i "s/version=$snapshotVersion/version=$nextVersion/" gradle.properties
git add gradle.properties > /dev/null
git commit -m"Next development version (v$nextVersion)" > /dev/null
fi;
echo "Staging Complete"
popd > /dev/null
+30
View File
@@ -0,0 +1,30 @@
---
platform: linux
image_resource:
type: registry-image
source:
repository: concourse/oci-build-task
tag: 0.10.0
username: ((docker-hub-username))
password: ((docker-hub-password))
inputs:
- name: ci-images-git-repo
outputs:
- name: image
caches:
- path: ci-image-cache
params:
CONTEXT: ci-images-git-repo/ci/images
DOCKERFILE: ci-images-git-repo/ci/images/ci-image/Dockerfile
DOCKER_HUB_AUTH: ((docker-hub-auth))
run:
path: /bin/sh
args:
- "-c"
- |
mkdir -p /root/.docker
cat > /root/.docker/config.json <<EOF
{ "auths": { "https://index.docker.io/v1/": { "auth": "$DOCKER_HUB_AUTH" }}}
EOF
build
+22
View File
@@ -0,0 +1,22 @@
---
platform: linux
image_resource:
type: registry-image
source:
repository: springio/github-changelog-generator
tag: '0.0.8'
username: ((docker-hub-username))
password: ((docker-hub-password))
inputs:
- name: git-repo
- name: artifactory-repo
outputs:
- name: generated-changelog
params:
GITHUB_ORGANIZATION:
GITHUB_REPO:
GITHUB_USERNAME:
GITHUB_TOKEN:
RELEASE_TYPE:
run:
path: git-repo/ci/scripts/generate-changelog.sh
+25
View File
@@ -0,0 +1,25 @@
---
platform: linux
image_resource:
type: registry-image
source:
repository: springio/concourse-release-scripts
tag: '0.4.0'
username: ((docker-hub-username))
password: ((docker-hub-password))
inputs:
- name: git-repo
- name: artifactory-repo
outputs:
- name: version
params:
RELEASE_TYPE:
ARTIFACTORY_SERVER:
ARTIFACTORY_USERNAME:
ARTIFACTORY_PASSWORD:
SONATYPE_USER:
SONATYPE_PASSWORD:
SONATYPE_URL:
SONATYPE_STAGING_PROFILE:
run:
path: git-repo/ci/scripts/promote-version.sh
+17
View File
@@ -0,0 +1,17 @@
---
platform: linux
inputs:
- name: git-repo
outputs:
- name: stage-git-repo
- name: distribution-repository
params:
RELEASE_TYPE:
CI: true
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
caches:
- path: gradle
run:
path: git-repo/ci/scripts/stage-version.sh
+2 -2
View File
@@ -28,7 +28,7 @@ javadoc {
header = rootProject.description
use = true
overview = project.relativePath("$rootProject.rootDir/framework-docs/src/docs/api/overview.html")
destinationDir = project.java.docsDir.dir("javadoc-api").get().asFile
destinationDir = file("$project.docsDir/javadoc-api")
splitIndex = true
links(rootProject.ext.javadocLinks)
addBooleanOption('Xdoclint:syntax,reference', true) // only check syntax and reference with doclint
@@ -52,7 +52,7 @@ rootProject.tasks.dokkaHtmlMultiModule.configure {
tasks.named("javadoc")
}
moduleName.set("spring-framework")
outputDirectory.set(project.java.docsDir.dir("kdoc-api").get().asFile)
outputDirectory.set(file("$docsDir/kdoc-api"))
includes.from("$rootProject.rootDir/framework-docs/src/docs/api/dokka-overview.md")
}
-39
View File
@@ -1,39 +0,0 @@
antora:
extensions:
- require: '@springio/antora-extensions'
root_component_name: 'framework'
site:
title: Spring Framework
url: https://docs.spring.io/spring-framework/reference
robots: allow
git:
ensure_git_suffix: false
content:
sources:
- url: https://github.com/spring-projects/spring-framework
# Refname matching:
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
branches: ['main', '{6..9}.+({1..9}).x']
tags: ['v{6..9}.+({0..9}).+({0..9})?(-{RC,M}*)', '!(v6.0.{0..8})', '!(v6.0.0-{RC,M}{0..9})']
start_path: framework-docs
asciidoc:
extensions:
- '@asciidoctor/tabs'
- '@springio/asciidoctor-extensions'
- '@springio/asciidoctor-extensions/include-code-extension'
attributes:
page-stackoverflow-url: https://stackoverflow.com/questions/tagged/spring
page-pagination: ''
hide-uri-scheme: '@'
tabs-sync-option: '@'
include-java: 'example$docs-src/main/java/org/springframework/docs'
urls:
latest_version_segment_strategy: redirect:to
latest_version_segment: ''
redirect_facility: httpd
runtime:
log:
failure_level: warn
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.17/ui-bundle.zip
+2 -5
View File
@@ -21,6 +21,7 @@ asciidoc:
table-stripes: 'odd'
include-java: 'example$docs-src/main/java/org/springframework/docs'
include-kotlin: 'example$docs-src/main/kotlin/org/springframework/docs'
include-xml: 'example$docs-src/main/resources/org/springframework/docs'
spring-site: 'https://spring.io'
spring-site-blog: '{spring-site}/blog'
spring-site-cve: "{spring-site}/security"
@@ -71,9 +72,6 @@ asciidoc:
kotlin-coroutines-api: '{kotlin-site}/api/kotlinx.coroutines'
kotlin-github-org: 'https://github.com/Kotlin'
kotlin-issues: 'https://youtrack.jetbrains.com/issue'
micrometer-docs: 'https://docs.micrometer.io/micrometer/reference'
micrometer-context-propagation-docs: 'https://docs.micrometer.io/context-propagation/reference'
petclinic-github-org: 'https://github.com/spring-petclinic'
reactive-streams-site: 'https://www.reactive-streams.org'
reactive-streams-spec: 'https://github.com/reactive-streams/reactive-streams-jvm/blob/master/README.md#specification'
reactor-github-org: 'https://github.com/reactor'
@@ -91,5 +89,4 @@ asciidoc:
stackoverflow-questions: '{stackoverflow-site}/questions'
stackoverflow-spring-tag: "{stackoverflow-questions}/tagged/spring"
stackoverflow-spring-kotlin-tags: "{stackoverflow-spring-tag}+kotlin"
testcontainers-site: 'https://www.testcontainers.org'
vavr-docs: 'https://vavr-io.github.io/vavr-docs'
testcontainers-site: 'https://www.testcontainers.org'
+36 -6
View File
@@ -10,10 +10,27 @@ apply from: "${rootDir}/gradle/ide.gradle"
apply from: "${rootDir}/gradle/publications.gradle"
antora {
options = [clean: true, fetch: !project.gradle.startParameter.offline, stacktrace: true]
version = '3.2.0-alpha.2'
playbook = 'cached-antora-playbook.yml'
playbookProvider {
repository = 'spring-projects/spring-framework'
branch = 'docs-build'
path = 'lib/antora/templates/per-branch-antora-playbook.yml'
checkLocalBranch = true
}
options = ['--clean', '--stacktrace']
environment = [
'BUILD_REFNAME': 'HEAD',
'BUILD_VERSION': project.version,
'ALGOLIA_API_KEY': '82c7ead946afbac3cf98c32446154691',
'ALGOLIA_APP_ID': '244V8V9FGG',
'ALGOLIA_INDEX_NAME': 'framework-docs'
]
dependencies = [
'@antora/atlas-extension': '1.0.0-alpha.1',
'@antora/collector-extension': '1.0.0-alpha.3',
'@asciidoctor/tabs': '1.0.0-beta.3',
'@opendevise/antora-release-line-extension': '1.0.0',
'@springio/antora-extensions': '1.8.2',
'@springio/asciidoctor-extensions': '1.0.0-alpha.10'
]
}
@@ -46,12 +63,25 @@ dependencies {
api(project(":spring-jdbc"))
api(project(":spring-jms"))
api(project(":spring-web"))
api(project(":spring-webflux"))
api(project(":spring-webmvc"))
api(project(":spring-context-support"))
api(project(":spring-aspects"))
api(project(":spring-websocket"))
api("com.oracle.database.jdbc:ojdbc11")
api("org.jetbrains.kotlin:kotlin-stdlib")
api("jakarta.jms:jakarta.jms-api")
api("jakarta.servlet:jakarta.servlet-api")
api("org.jetbrains.kotlin:kotlin-stdlib")
api("org.apache.commons:commons-dbcp2:2.11.0")
api("com.mchange:c3p0:0.9.5.5")
api("com.fasterxml.jackson.core:jackson-databind")
api("com.fasterxml.jackson.module:jackson-module-parameter-names")
api("jakarta.validation:jakarta.validation-api")
api("org.aspectj:aspectjweaver")
api("io.projectreactor.netty:reactor-netty-http")
api("org.eclipse.jetty.websocket:jetty-websocket-jetty-api")
api("javax.cache:cache-api")
api("jakarta.resource:jakarta.resource-api")
api("org.apache.activemq:activemq-ra:6.1.2")
implementation(project(":spring-core-test"))
implementation("org.assertj:assertj-core")
+3
View File
@@ -134,6 +134,7 @@
**** 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/bean-overriding.adoc[]
*** xref:testing/testcontext-framework/web-scoped-beans.adoc[]
*** xref:testing/testcontext-framework/tx.adoc[]
*** xref:testing/testcontext-framework/executing-sql.adoc[]
@@ -171,6 +172,8 @@
***** xref:testing/annotations/integration-spring/annotation-activeprofiles.adoc[]
***** xref:testing/annotations/integration-spring/annotation-testpropertysource.adoc[]
***** xref:testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc[]
***** xref:testing/annotations/integration-spring/annotation-testbean.adoc[]
***** xref:testing/annotations/integration-spring/annotation-mockitobean.adoc[]
***** xref:testing/annotations/integration-spring/annotation-dirtiescontext.adoc[]
***** xref:testing/annotations/integration-spring/annotation-testexecutionlisteners.adoc[]
***** xref:testing/annotations/integration-spring/annotation-recordapplicationevents.adoc[]
@@ -23,12 +23,6 @@ The following table lists all currently supported Spring properties.
|===
| Name | Description
| `spring.aop.ajc.ignore`
| Instructs Spring to ignore ajc-compiled aspects for Spring AOP proxying, restoring traditional
Spring behavior for scenarios where both weaving and AspectJ auto-proxying are enabled. See
{spring-framework-api}++/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.html#IGNORE_AJC_PROPERTY_NAME++[`AbstractAspectJAdvisorFactory`]
for details.
| `spring.aot.enabled`
| Indicates the application should run with AOT generated artifacts. See
xref:core/aot.adoc[Ahead of Time Optimizations] and
@@ -38,7 +32,7 @@ for details.
| `spring.beaninfo.ignore`
| Instructs Spring to use the `Introspector.IGNORE_ALL_BEANINFO` mode when calling the
JavaBeans `Introspector`. See
{spring-framework-api}++/beans/StandardBeanInfoFactory.html#IGNORE_BEANINFO_PROPERTY_NAME++[`StandardBeanInfoFactory`]
{spring-framework-api}++/beans/StandardBeanInfoFactory.html#IGNORE_BEANINFO_PROPERTY_NAME++[`CachedIntrospectionResults`]
for details.
| `spring.cache.reactivestreams.ignore`
@@ -55,13 +49,15 @@ for details.
| `spring.context.checkpoint`
| Property that specifies a common context checkpoint. See
xref:integration/checkpoint-restore.adoc#_automatic_checkpointrestore_at_startup[Automatic checkpoint/restore at startup] and
xref:integration/checkpoint-restore.adoc#_automatic_checkpointrestore_at_startup[Automatic
checkpoint/restore at startup] and
{spring-framework-api}++/context/support/DefaultLifecycleProcessor.html#CHECKPOINT_PROPERTY_NAME++[`DefaultLifecycleProcessor`]
for details.
| `spring.context.exit`
| Property for terminating the JVM when the context reaches a specific phase. See
xref:integration/checkpoint-restore.adoc#_automatic_checkpointrestore_at_startup[Automatic checkpoint/restore at startup] and
xref:integration/checkpoint-restore.adoc#_automatic_checkpointrestore_at_startup[Automatic
checkpoint/restore at startup] and
{spring-framework-api}++/context/support/DefaultLifecycleProcessor.html#EXIT_PROPERTY_NAME++[`DefaultLifecycleProcessor`]
for details.
@@ -205,7 +205,7 @@ Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val person = factory.getBean("person") as Person;
val person = factory.getBean("person") as Person
----
======
@@ -128,18 +128,7 @@ the resulting pointcut is effectively the union of the specified patterns.)
The following example shows how to use `JdkRegexpMethodPointcut`:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="settersAndAbsquatulatePointcut"
class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
----
include-code::./JdkRegexpConfiguration[tag=snippet,indent=0]
Spring provides a convenience class named `RegexpMethodPointcutAdvisor`, which lets us
also reference an `Advice` (remember that an `Advice` can be an interceptor, before advice,
@@ -147,21 +136,7 @@ throws advice, and others). Behind the scenes, Spring uses a `JdkRegexpMethodPoi
Using `RegexpMethodPointcutAdvisor` simplifies wiring, as the one bean encapsulates both
pointcut and advice, as the following example shows:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="settersAndAbsquatulateAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="beanNameOfAopAllianceInterceptor"/>
</property>
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
----
include-code::./RegexpConfiguration[tag=snippet,indent=0]
You can use `RegexpMethodPointcutAdvisor` with any `Advice` type.
@@ -221,8 +221,8 @@ NOTE: `ThreadLocal` instances come with serious issues (potentially resulting in
incorrectly using them in multi-threaded and multi-classloader environments. You
should always consider wrapping a `ThreadLocal` in some other class and never directly use
the `ThreadLocal` itself (except in the wrapper class). Also, you should
always remember to correctly set and unset (where the latter involves a call to
`ThreadLocal.remove()`) the resource local to the thread. Unsetting should be done in
always remember to correctly set and unset (where the latter simply involves a call to
`ThreadLocal.set(null)`) the resource local to the thread. Unsetting should be done in
any case, since not unsetting it might result in problematic behavior. Spring's
`ThreadLocal` support does this for you and should always be considered in favor of using
`ThreadLocal` instances without other proper handling code.
@@ -8,54 +8,8 @@ determines that a bean is advised by one or more aspects, it automatically gener
a proxy for that bean to intercept method invocations and ensures that advice is run
as needed.
The @AspectJ support can be enabled with XML- or Java-style configuration. In either
case, you also need to ensure that AspectJ's `aspectjweaver.jar` library is on the
classpath of your application (version 1.9 or later). This library is available in the
`lib` directory of an AspectJ distribution or from the Maven Central repository.
[[aop-enable-aspectj-java]]
== Enabling @AspectJ Support with Java Configuration
To enable @AspectJ support with Java `@Configuration`, add the `@EnableAspectJAutoProxy`
annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableAspectJAutoProxy
class AppConfig
----
======
[[aop-enable-aspectj-xml]]
== Enabling @AspectJ Support with XML Configuration
To enable @AspectJ support with XML-based configuration, use the `aop:aspectj-autoproxy`
element, as the following example shows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspectj-autoproxy/>
----
This assumes that you use schema support as described in
xref:core/appendix/xsd-schemas.adoc[XML Schema-based configuration].
See xref:core/appendix/xsd-schemas.adoc#aop[the AOP schema] for how to
import the tags in the `aop` namespace.
The @AspectJ support can be enabled with programmatic or XML configuration. In either
case, you also need to ensure that AspectJ's `org.aspectj:aspectjweaver` library is on the
classpath of your application (version 1.9 or later).
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
@@ -9,43 +9,12 @@ minimal steps required for a not-very-useful aspect.
The first of the two examples shows a regular bean definition in the application context
that points to a bean class that is annotated with `@Aspect`:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="myAspect" class="com.xyz.NotVeryUsefulAspect">
<!-- configure properties of the aspect here -->
</bean>
----
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
The second of the two examples shows the `NotVeryUsefulAspect` class definition, which is
annotated with `@Aspect`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages",fold="none"]
----
package com.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class NotVeryUsefulAspect {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages",fold="none"]
----
package com.xyz
import org.aspectj.lang.annotation.Aspect
@Aspect
class NotVeryUsefulAspect
----
======
include-code::./NotVeryUsefulAspect[tag=snippet,indent=0]
Aspects (classes annotated with `@Aspect`) can have methods and fields, the same as any
other class. They can also contain pointcut, advice, and introduction (inter-type)
@@ -16,93 +16,9 @@ aspect.
Because we want to retry the operation, we need to use around advice so that we can
call `proceed` multiple times. The following listing shows the basic aspect implementation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Aspect
public class ConcurrentOperationExecutor implements Ordered {
include-code::./ConcurrentOperationExecutor[tag=snippet,indent=0]
private static final int DEFAULT_MAX_RETRIES = 2;
private int maxRetries = DEFAULT_MAX_RETRIES;
private int order = 1;
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
@Around("com.xyz.CommonPointcuts.businessService()") // <1>
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
int numAttempts = 0;
PessimisticLockingFailureException lockFailureException;
do {
numAttempts++;
try {
return pjp.proceed();
}
catch(PessimisticLockingFailureException ex) {
lockFailureException = ex;
}
} while(numAttempts <= this.maxRetries);
throw lockFailureException;
}
}
----
<1> References the `businessService` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Aspect
class ConcurrentOperationExecutor : Ordered {
private val DEFAULT_MAX_RETRIES = 2
private var maxRetries = DEFAULT_MAX_RETRIES
private var order = 1
fun setMaxRetries(maxRetries: Int) {
this.maxRetries = maxRetries
}
override fun getOrder(): Int {
return this.order
}
fun setOrder(order: Int) {
this.order = order
}
@Around("com.xyz.CommonPointcuts.businessService()") // <1>
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any? {
var numAttempts = 0
var lockFailureException: PessimisticLockingFailureException
do {
numAttempts++
try {
return pjp.proceed()
} catch (ex: PessimisticLockingFailureException) {
lockFailureException = ex
}
} while (numAttempts <= this.maxRetries)
throw lockFailureException
}
}
----
<1> References the `businessService` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
======
`@Around("com.xyz.CommonPointcuts.businessService()")` references the `businessService` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
Note that the aspect implements the `Ordered` interface so that we can set the precedence of
the aspect higher than the transaction advice (we want a fresh transaction each time we
@@ -114,70 +30,15 @@ we have exhausted all of our retry attempts.
The corresponding Spring configuration follows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspectj-autoproxy/>
<bean id="concurrentOperationExecutor"
class="com.xyz.service.impl.ConcurrentOperationExecutor">
<property name="maxRetries" value="3"/>
<property name="order" value="100"/>
</bean>
----
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
To refine the aspect so that it retries only idempotent operations, we might define the following
`Idempotent` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Retention(RetentionPolicy.RUNTIME)
// marker annotation
public @interface Idempotent {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Retention(AnnotationRetention.RUNTIME)
// marker annotation
annotation class Idempotent
----
======
include-code::./service/Idempotent[tag=snippet,indent=0]
We can then use the annotation to annotate the implementation of service operations. The change
to the aspect to retry only idempotent operations involves refining the pointcut
expression so that only `@Idempotent` operations match, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Around("execution(* com.xyz..service.*.*(..)) && " +
"@annotation(com.xyz.service.Idempotent)")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Around("execution(* com.xyz..service.*.*(..)) && " +
"@annotation(com.xyz.service.Idempotent)")
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any? {
// ...
}
----
======
include-code::./service/SampleService[tag=snippet,indent=0]
@@ -72,7 +72,7 @@ Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
val usageTracked = context.getBean("myService", UsageTracked.class)
val usageTracked = context.getBean<UsageTracked>("myService")
----
======
@@ -66,7 +66,7 @@ Kotlin::
When used as a marker interface in this way, Spring configures new instances of the
annotated type (`Account`, in this case) by using a bean definition (typically
prototype-scoped) with the same name as the fully-qualified type name
(`com.xyz.domain.Account`). Since the default name for a bean is the
(`com.xyz.domain.Account`). Since the default name for a bean defined via XML is the
fully-qualified name of its type, a convenient way to declare the prototype definition
is to omit the `id` attribute, as the following example shows:
@@ -177,41 +177,10 @@ either use a build-time Ant or Maven task to do this (see, for example, the
{aspectj-docs-devguide}/antTasks.html[AspectJ Development
Environment Guide]) or load-time weaving (see xref:core/aop/using-aspectj.adoc#aop-aj-ltw[Load-time Weaving with AspectJ in the Spring Framework]). The
`AnnotationBeanConfigurerAspect` itself needs to be configured by Spring (in order to obtain
a reference to the bean factory that is to be used to configure new objects). If you
use Java-based configuration, you can add `@EnableSpringConfigured` to any
`@Configuration` class, as follows:
a reference to the bean factory that is to be used to configure new objects). You can define
the related configuration as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableSpringConfigured
public class AppConfig {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableSpringConfigured
class AppConfig {
}
----
======
If you prefer XML based configuration, the Spring
xref:core/appendix/xsd-schemas.adoc#context[`context` namespace]
defines a convenient `context:spring-configured` element, which you can use as follows:
[source,xml,indent=0,subs="verbatim"]
----
<context:spring-configured/>
----
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
Instances of `@Configurable` objects created before the aspect has been configured
result in a message being issued to the debug log and no configuration of the
@@ -420,7 +389,7 @@ who typically are in charge of the deployment configuration, such as the launch
Now that the sales pitch is over, let us first walk through a quick example of AspectJ
LTW that uses Spring, followed by detailed specifics about elements introduced in the
example. For a complete example, see the
{petclinic-github-org}/spring-framework-petclinic[Petclinic sample application based on Spring Framework].
{spring-github-org}/spring-petclinic[Petclinic sample application].
[[aop-aj-ltw-first-example]]
@@ -783,52 +752,9 @@ adding one line. (Note that you almost certainly need to use an
`ApplicationContext` as your Spring container -- typically, a `BeanFactory` is not
enough because the LTW support uses `BeanFactoryPostProcessors`.)
To enable the Spring Framework's LTW support, you need to configure a `LoadTimeWeaver`,
which typically is done by using the `@EnableLoadTimeWeaving` annotation, as follows:
To enable the Spring Framework's LTW support, you need to configure a `LoadTimeWeaver` as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableLoadTimeWeaving
class AppConfig {
}
----
======
Alternatively, if you prefer XML-based configuration, use the
`<context:load-time-weaver/>` element. Note that the element is defined in the
`context` namespace. The following example shows how to use `<context:load-time-weaver/>`:
[source,xml,indent=0,subs="verbatim"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:load-time-weaver/>
</beans>
----
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
The preceding configuration automatically defines and registers a number of LTW-specific
infrastructure beans, such as a `LoadTimeWeaver` and an `AspectJWeavingEnabler`, for you.
@@ -864,63 +790,12 @@ Note that the table lists only the `LoadTimeWeavers` that are autodetected when
use the `DefaultContextLoadTimeWeaver`. You can specify exactly which `LoadTimeWeaver`
implementation to use.
To specify a specific `LoadTimeWeaver` with Java configuration, implement the
`LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method.
To configure a specific `LoadTimeWeaver`, implement the
`LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method
(or use the XML equivalent).
The following example specifies a `ReflectiveLoadTimeWeaver`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableLoadTimeWeaving
public class AppConfig implements LoadTimeWeavingConfigurer {
@Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new ReflectiveLoadTimeWeaver();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableLoadTimeWeaving
class AppConfig : LoadTimeWeavingConfigurer {
override fun getLoadTimeWeaver(): LoadTimeWeaver {
return ReflectiveLoadTimeWeaver()
}
}
----
======
If you use XML-based configuration, you can specify the fully qualified class name
as the value of the `weaver-class` attribute on the `<context:load-time-weaver/>`
element. Again, the following example specifies a `ReflectiveLoadTimeWeaver`:
[source,xml,indent=0,subs="verbatim"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:load-time-weaver
weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
</beans>
----
include-code::./CustomWeaverConfiguration[tag=snippet,indent=0]
The `LoadTimeWeaver` that is defined and registered by the configuration can be later
retrieved from the Spring container by using the well known name, `loadTimeWeaver`.
@@ -36,7 +36,7 @@ NOTE: At the moment, AOT is focused on allowing Spring applications to be deploy
We intend to support more JVM-based use cases in future generations.
[[aot.basics]]
== AOT Engine Overview
== AOT engine overview
The entry point of the AOT engine for processing an `ApplicationContext` is `ApplicationContextAotGenerator`. It takes care of the following steps, based on a `GenericApplicationContext` that represents the application to optimize and a {spring-framework-api}/aot/generate/GenerationContext.html[`GenerationContext`]:
@@ -225,7 +225,7 @@ When a `datasource` instance is required, a `BeanInstanceSupplier` is called.
This supplier invokes the `dataSource()` method on the `dataSourceConfiguration` bean.
[[aot.running]]
== Running with AOT Optimizations
== Running with AOT optimizations
AOT is a mandatory step to transform a Spring application to a native executable, so it
is automatically enabled when running in this mode. It is possible to use those optimizations
@@ -244,7 +244,7 @@ However, keep in mind that some optimizations are made at build time based on a
This section lists the best practices that make sure your application is ready for AOT.
[[aot.bestpractices.bean-registration]]
=== Programmatic Bean Registration
== Programmatic bean registration
The AOT engine takes care of the `@Configuration` model and any callback that might be
invoked as part of processing your configuration. If you need to register additional
@@ -266,7 +266,7 @@ notion of a classpath. For cases like this, it is crucial that the scanning happ
build time.
[[aot.bestpractices.bean-type]]
=== Expose the Most Precise Bean Type
=== Expose The Most Precise Bean Type
While your application may interact with an interface that a bean implements, it is still very important to declare the most precise type.
The AOT engine performs additional checks on the bean type, such as detecting the presence of `@Autowired` members or lifecycle callback methods.
@@ -326,21 +326,8 @@ However, this is not a best practice and flagging the preferred constructor with
In case you are working on a code base that you cannot modify, you can set the {spring-framework-api}/beans/factory/support/AbstractBeanDefinition.html#PREFERRED_CONSTRUCTORS_ATTRIBUTE[`preferredConstructors` attribute] on the related bean definition to indicate which constructor should be used.
[[aot.bestpractices.complex-data-structures]]
=== Avoid Complex Data Structures for Constructor Parameters and Properties
When crafting a `RootBeanDefinition` programmatically, you are not constrained in terms of types that you can use.
For instance, you may have a custom `record` with several properties that your bean takes as a constructor argument.
While this works fine with the regular runtime, AOT does not know how to generate the code of your custom data structure.
A good rule of thumb is to keep in mind that bean definitions are an abstraction on top of several models.
Rather than using such structures, decomposing to simple types or referring to a bean that is built as such is recommended.
As a last resort, you can implement your own `org.springframework.aot.generate.ValueCodeGenerator$Delegate`.
To use it, register its fully qualified name in `META-INF/spring/aot.factories` using the `Delegate` as the key.
[[aot.bestpractices.custom-arguments]]
=== Avoid Creating Beans with Custom Arguments
=== Avoid Creating Bean with Custom Arguments
Spring AOT detects what needs to be done to create a bean and translates that in generated code using an instance supplier.
The container also supports creating a bean with {spring-framework-api}++/beans/factory/BeanFactory.html#getBean(java.lang.String,java.lang.Object...)++[custom arguments] that leads to several issues with AOT:
@@ -352,20 +339,6 @@ For instance, autowiring on fields and methods will be skipped as they are handl
Rather than having prototype-scoped beans created with custom arguments, we recommend a manual factory pattern where a bean is responsible for the creation of the instance.
[[aot.bestpractices.circular-dependencies]]
=== Avoid Circular Dependencies
Certain use cases can result in circular dependencies between one or more beans. With the
regular runtime, it may be possible to wire those circular dependencies via `@Autowired`
on setter methods or fields. However, an AOT-optimized context will fail to start with
explicit circular dependencies.
In an AOT-optimized application, you should therefore strive to avoid circular
dependencies. If that is not possible, you can use `@Lazy` injection points or
`ObjectProvider` to lazily access or retrieve the necessary collaborating beans. See
xref:core/beans/classpath-scanning.adoc#beans-factorybeans-annotations-lazy-injection-points[this tip]
for further information.
[[aot.bestpractices.factory-bean]]
=== FactoryBean
@@ -1,5 +1,5 @@
[[beans-autowired-annotation-primary]]
= Fine-tuning Annotation-based Autowiring with `@Primary`
= Fine-tuning Annotation-based Autowiring with `@Primary` or `@Fallback`
Because autowiring by type may lead to multiple candidates, it is often necessary to have
more control over the selection process. One way to accomplish this is with Spring's
@@ -50,8 +50,51 @@ Kotlin::
----
======
With the preceding configuration, the following `MovieRecommender` is autowired with the
`firstMovieCatalog`:
Alternatively, as of 6.2, there is a `@Fallback` annotation for demarcating
any beans other than the regular ones to be injected. If only one regular
bean is left, it is effectively primary as well:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class MovieConfiguration {
@Bean
public MovieCatalog firstMovieCatalog() { ... }
@Bean
@Fallback
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class MovieConfiguration {
@Bean
fun firstMovieCatalog(): MovieCatalog { ... }
@Bean
@Fallback
fun secondMovieCatalog(): MovieCatalog { ... }
// ...
}
----
======
With both variants of the preceding configuration, the following
`MovieRecommender` is autowired with the `firstMovieCatalog`:
[tabs]
======
@@ -1,12 +1,13 @@
[[beans-autowired-annotation-qualifiers]]
= Fine-tuning Annotation-based Autowiring with Qualifiers
`@Primary` is an effective way to use autowiring by type with several instances when one
primary candidate can be determined. When you need more control over the selection process,
you can use Spring's `@Qualifier` annotation. You can associate qualifier values
with specific arguments, narrowing the set of type matches so that a specific bean is
chosen for each argument. In the simplest case, this can be a plain descriptive value, as
shown in the following example:
`@Primary` and `@Fallback` are effective ways to use autowiring by type with several
instances when one primary (or non-fallback) candidate can be determined.
When you need more control over the selection process, you can use Spring's `@Qualifier`
annotation. You can associate qualifier values with specific arguments, narrowing the set
of type matches so that a specific bean is chosen for each argument. In the simplest case,
this can be a plain descriptive value, as shown in the following example:
--
[tabs]
@@ -153,15 +154,17 @@ Letting qualifier values select against target bean names, within the type-match
candidates, does not require a `@Qualifier` annotation at the injection point.
If there is no other resolution indicator (such as a qualifier or a primary marker),
for a non-unique dependency situation, Spring matches the injection point name
(that is, the field name or parameter name) against the target bean names and chooses
the same-named candidate, if any (either by bean name or by associated alias).
(that is, the field name or parameter name) against the target bean names and chooses the
same-named candidate, if any.
Since version 6.1, this requires the `-parameters` Java compiler flag to be present.
====
As an alternative for injection by name, consider the JSR-250 `@Resource` annotation
which is semantically defined to identify a specific target component by its unique name,
with the declared type being irrelevant for the matching process. `@Autowired` has rather
That said, if you intend to express annotation-driven injection by name, do not
primarily use `@Autowired`, even if it is capable of selecting by bean name among
type-matching candidates. Instead, use the JSR-250 `@Resource` annotation, which is
semantically defined to identify a specific target component by its unique name, with
the declared type being irrelevant for the matching process. `@Autowired` has rather
different semantics: After selecting candidate beans by type, the specified `String`
qualifier value is considered within those type-selected candidates only (for example,
matching an `account` qualifier against beans marked with the same qualifier label).
@@ -101,8 +101,8 @@ NOTE: When configuring a `PropertySourcesPlaceholderConfigurer` using JavaConfig
Using the above configuration ensures Spring initialization failure if any `${}`
placeholder could not be resolved. It is also possible to use methods like
`setPlaceholderPrefix`, `setPlaceholderSuffix`, or `setValueSeparator` to customize
placeholders.
`setPlaceholderPrefix`, `setPlaceholderSuffix`, `setValueSeparator`, or
`setEscapeCharacter` to customize placeholders.
NOTE: Spring Boot configures by default a `PropertySourcesPlaceholderConfigurer` bean that
will get properties from `application.properties` and `application.yml` files.
@@ -317,8 +317,8 @@ exposed based on security policies in some environments -- for example, standalo
JDK 1.7.0_45 and higher (which requires 'Trusted-Library' setup in your manifests -- see
{stackoverflow-questions}/19394570/java-jre-7u45-breaks-classloader-getresources).
On the module path (Java Module System), Spring's classpath scanning generally works as
expected. However, make sure that your component classes are exported in your `module-info`
On JDK 9's module path (Jigsaw), Spring's classpath scanning generally works as expected.
However, make sure that your component classes are exported in your `module-info`
descriptors. If you expect Spring to invoke non-public members of your classes, make
sure that they are 'opened' (that is, that they use an `opens` declaration instead of an
`exports` declaration in your `module-info` descriptor).
@@ -480,15 +480,11 @@ factory method and other bean definition properties, such as a qualifier value t
the `@Qualifier` annotation. Other method-level annotations that can be specified are
`@Scope`, `@Lazy`, and custom qualifier annotations.
[[beans-factorybeans-annotations-lazy-injection-points]]
[TIP]
====
In addition to its role for component initialization, you can also place the `@Lazy`
TIP: In addition to its role for component initialization, you can also place the `@Lazy`
annotation on injection points marked with `@Autowired` or `@Inject`. In this context,
it leads to the injection of a lazy-resolution proxy. However, such a proxy approach
is rather limited. For sophisticated lazy interactions, in particular in combination
with optional dependencies, we recommend `ObjectProvider<MyTargetBean>` instead.
====
Autowired fields and methods are supported, as previously discussed, with additional
support for autowiring of `@Bean` methods. The following example shows how to do so:
@@ -755,7 +751,7 @@ and bean definition show.
TIP: If you run into naming conflicts due to multiple autodetected components having the
same non-qualified class name (i.e., classes with identical names but residing in
different packages), you may need to configure a `BeanNameGenerator` that defaults to the
fully qualified class name for the generated bean name. The
fully qualified class name for the generated bean name. As of Spring Framework 5.2.3, the
`FullyQualifiedAnnotationBeanNameGenerator` located in package
`org.springframework.context.annotation` can be used for such purposes.
@@ -924,8 +920,7 @@ Kotlin::
[[beans-scanning-qualifiers]]
== Providing Qualifier Metadata with Annotations
The `@Qualifier` annotation is discussed in
xref:core/beans/annotation-config/autowired-qualifiers.adoc[Fine-tuning Annotation-based Autowiring with Qualifiers].
The `@Qualifier` annotation is discussed in xref:core/beans/annotation-config/autowired-qualifiers.adoc[Fine-tuning Annotation-based Autowiring with Qualifiers].
The examples in that section demonstrate the use of the `@Qualifier` annotation and
custom qualifier annotations to provide fine-grained control when you resolve autowire
candidates. Because those examples were based on XML bean definitions, the qualifier
@@ -1,7 +1,7 @@
[[context-introduction]]
= Additional Capabilities of the `ApplicationContext`
As discussed in the xref:core/beans/introduction.adoc[chapter introduction], the `org.springframework.beans.factory`
As discussed in the xref:web/webmvc-view/mvc-xslt.adoc#mvc-view-xslt-beandefs[chapter introduction], the `org.springframework.beans.factory`
package provides basic functionality for managing and manipulating beans, including in a
programmatic way. The `org.springframework.context` package adds the
{spring-framework-api}/context/ApplicationContext.html[`ApplicationContext`]
@@ -644,7 +644,7 @@ Each `SpEL` expression evaluates against a dedicated context. The following tabl
items made available to the context so that you can use them for conditional event processing:
[[context-functionality-events-annotation-tbl]]
.Event metadata available in SpEL expressions
.Event SpEL available metadata
|===
| Name| Location| Description| Example
@@ -660,8 +660,8 @@ items made available to the context so that you can use them for conditional eve
| __Argument name__
| evaluation context
| The name of a particular method argument. If the names are not available
(for example, because the code was compiled without the `-parameters` flag), individual
| The name of any of the method arguments. If, for some reason, the names are not available
(for example, because there is no debug information in the compiled byte code), individual
arguments are also available using the `#a<#arg>` syntax where `<#arg>` stands for the
argument index (starting from 0).
| `#blEvent` or `#a0` (you can also use `#p0` or `#p<#arg>` parameter notation as an alias)
@@ -883,12 +883,11 @@ e.g. for processing all events asynchronously and/or for handling listener excep
== Convenient Access to Low-level Resources
For optimal usage and understanding of application contexts, you should familiarize
yourself with Spring's `Resource` abstraction, as described in
xref:core/resources.adoc[Resources].
yourself with Spring's `Resource` abstraction, as described in xref:web/webflux-webclient/client-builder.adoc#webflux-client-builder-reactor-resources[Resources].
An application context is a `ResourceLoader`, which can be used to load `Resource` objects.
A `Resource` is essentially a more feature rich version of the JDK `java.net.URL` class.
In fact, implementations of `Resource` wrap an instance of `java.net.URL`, where
In fact, the implementations of the `Resource` wrap an instance of `java.net.URL`, where
appropriate. A `Resource` can obtain low-level resources from almost any location in a
transparent fashion, including from the classpath, a filesystem location, anywhere
describable with a standard URL, and some other variations. If the resource location
@@ -73,8 +73,6 @@ runtime (concurrently with live access to the factory) is not officially support
lead to concurrent access exceptions, inconsistent state in the bean container, or both.
====
[[beans-definition-overriding]]
== Overriding Beans
@@ -83,22 +81,15 @@ already allocated. While bean overriding is possible, it makes the configuration
to read and this feature will be deprecated in a future release.
To disable bean overriding altogether, you can set the `allowBeanDefinitionOverriding`
flag to `false` on the `ApplicationContext` before it is refreshed. In such setup, an
to `false` on the `ApplicationContext` before it is refreshed. In such setup, an
exception is thrown if bean overriding is used.
By default, the container logs every bean overriding at `INFO` level so that you can
adapt your configuration accordingly. While not recommended, you can silence those logs
by setting the `allowBeanDefinitionOverriding` flag to `true`.
.Java-configuration
****
If you use Java Configuration, a corresponding `@Bean` method always silently overrides
a scanned bean class with the same component name as long as the return type of the
`@Bean` method matches that bean class. This simply means that the container will call
the `@Bean` factory method in favor of any pre-declared constructor on the bean class.
****
NOTE: We acknowledge that overriding beans in a test is convenient, and there is
explicit support for this. For more details please refer to xref:testing/testcontext-framework/bean-overriding.adoc[this section].
[[beans-beanname]]
== Naming Beans
@@ -60,7 +60,6 @@ instance's values consist of all bean instances that match the expected type, an
`Map` instance's keys contain the corresponding bean names.
[[beans-autowired-exceptions]]
== Limitations and Disadvantages of Autowiring
@@ -102,10 +101,10 @@ In the latter scenario, you have several options:
== Excluding a Bean from Autowiring
On a per-bean basis, you can exclude a bean from autowiring. In Spring's XML format, set
the `autowire-candidate` attribute of the `<bean/>` element to `false`; with the `@Bean`
annotation, the attribute is named `autowireCandidate`. The container makes that specific
bean definition unavailable to the autowiring infrastructure, including annotation-based
injection points such as xref:core/beans/annotation-config/autowired.adoc[`@Autowired`].
the `autowire-candidate` attribute of the `<bean/>` element to `false`. The container
makes that specific bean definition unavailable to the autowiring infrastructure
(including annotation style configurations such as xref:core/beans/annotation-config/autowired.adoc[`@Autowired`]
).
NOTE: The `autowire-candidate` attribute is designed to only affect type-based autowiring.
It does not affect explicit references by name, which get resolved even if the
@@ -120,8 +119,8 @@ provide multiple patterns, define them in a comma-separated list. An explicit va
`true` or `false` for a bean definition's `autowire-candidate` attribute always takes
precedence. For such beans, the pattern matching rules do not apply.
These techniques are useful for beans that you never want to be injected into other beans
by autowiring. It does not mean that an excluded bean cannot itself be configured by
These techniques are useful for beans that you never want to be injected into other
beans by autowiring. It does not mean that an excluded bean cannot itself be configured by
using autowiring. Rather, the bean itself is not a candidate for autowiring other beans.
@@ -159,12 +159,10 @@ Kotlin::
----
======
[discrete]
[[beans-factory-ctor-arguments-type]]
==== Constructor argument type matching
.[[beans-factory-ctor-arguments-type]]Constructor argument type matching
--
In the preceding scenario, the container can use type matching with simple types if
you explicitly specify the type of the constructor argument via the `type` attribute,
you explicitly specify the type of the constructor argument by using the `type` attribute,
as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -174,11 +172,10 @@ as the following example shows:
<constructor-arg type="java.lang.String" value="42"/>
</bean>
----
--
[discrete]
[[beans-factory-ctor-arguments-index]]
==== Constructor argument index
.[[beans-factory-ctor-arguments-index]]Constructor argument index
--
You can use the `index` attribute to specify explicitly the index of constructor arguments,
as the following example shows:
@@ -194,11 +191,10 @@ In addition to resolving the ambiguity of multiple simple values, specifying an
resolves ambiguity where a constructor has two arguments of the same type.
NOTE: The index is 0-based.
--
[discrete]
[[beans-factory-ctor-arguments-name]]
==== Constructor argument name
.[[beans-factory-ctor-arguments-name]]Constructor argument name
--
You can also use the constructor parameter name for value disambiguation, as the following
example shows:
@@ -211,8 +207,8 @@ example shows:
----
Keep in mind that, to make this work out of the box, your code must be compiled with the
`-parameters` flag enabled so that Spring can look up the parameter name from the constructor.
If you cannot or do not want to compile your code with the `-parameters` flag, you can use the
debug flag enabled so that Spring can look up the parameter name from the constructor.
If you cannot or do not want to compile your code with the debug flag, you can use the
https://download.oracle.com/javase/8/docs/api/java/beans/ConstructorProperties.html[@ConstructorProperties]
JDK annotation to explicitly name your constructor arguments. The sample class would
then have to look as follows:
@@ -248,6 +244,7 @@ Kotlin::
constructor(val years: Int, val ultimateAnswer: String)
----
======
--
[[beans-setter-injection]]
@@ -2,15 +2,13 @@
= Using `depends-on`
If a bean is a dependency of another bean, that usually means that one bean is set as a
property of another. Typically you accomplish this with the
xref:core/beans/dependencies/factory-properties-detailed.adoc#beans-ref-element[`<ref/>` element>]
in XML-based metadata or through xref:core/beans/dependencies/factory-autowire.adoc[autowiring].
However, sometimes dependencies between beans are less direct. An example is when a static
initializer in a class needs to be triggered, such as for database driver registration.
The `depends-on` attribute or `@DependsOn` annotation can explicitly force one or more beans
to be initialized before the bean using this element is initialized. The following example
uses the `depends-on` attribute to express a dependency on a single bean:
property of another. Typically you accomplish this with the <<beans-ref-element, `<ref/>`
element>> in XML-based configuration metadata. However, sometimes dependencies between
beans are less direct. An example is when a static initializer in a class needs to be
triggered, such as for database driver registration. The `depends-on` attribute can
explicitly force one or more beans to be initialized before the bean using this element
is initialized. The following example uses the `depends-on` attribute to express a
dependency on a single bean:
[source,xml,indent=0,subs="verbatim,quotes"]
----
@@ -33,10 +31,10 @@ delimiters):
----
NOTE: The `depends-on` attribute can specify both an initialization-time dependency and,
in the case of xref:core/beans/factory-scopes.adoc#beans-factory-scopes-singleton[singleton]
beans only, a corresponding destruction-time dependency. Dependent beans that define a
`depends-on` relationship with a given bean are destroyed first, prior to the given bean
itself being destroyed. Thus, `depends-on` can also control shutdown order.
in the case of xref:core/beans/factory-scopes.adoc#beans-factory-scopes-singleton[singleton] beans only, a corresponding
destruction-time dependency. Dependent beans that define a `depends-on` relationship
with a given bean are destroyed first, prior to the given bean itself being destroyed.
Thus, `depends-on` can also control shutdown order.
@@ -10,33 +10,25 @@ pre-instantiation of a singleton bean by marking the bean definition as being
lazy-initialized. A lazy-initialized bean tells the IoC container to create a bean
instance when it is first requested, rather than at startup.
In XML, this behavior is controlled by the `lazy-init` attribute on the `<bean/>`
element, as the following example shows:
This behavior is controlled by the `@Lazy` annotation or in XML the `lazy-init` attribute on the `<bean/>` element, as
the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>
----
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
When the preceding configuration is consumed by an `ApplicationContext`, the `lazy` bean
is not eagerly pre-instantiated when the `ApplicationContext` starts,
whereas the `not.lazy` bean is eagerly pre-instantiated.
whereas the `notLazy` one is eagerly pre-instantiated.
However, when a lazy-initialized bean is a dependency of a singleton bean that is
not lazy-initialized, the `ApplicationContext` creates the lazy-initialized bean at
startup, because it must satisfy the singleton's dependencies. The lazy-initialized bean
is injected into a singleton bean elsewhere that is not lazy-initialized.
You can also control lazy-initialization at the container level by using the
`default-lazy-init` attribute on the `<beans/>` element, as the following example shows:
You can also control lazy-initialization for a set of beans by using the `@Lazy` annotation on your `@Configuration`
annotated class or in XML using the `default-lazy-init` attribute on the `<beans/>` element, as the following example
shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<beans default-lazy-init="true">
<!-- no beans will be pre-instantiated... -->
</beans>
----
include-code::./LazyConfiguration[tag=snippet,indent=0]
@@ -201,7 +201,7 @@ the original class. Consider the following example:
<!-- inject dependencies here as required -->
</bean>
<!-- commandManager uses myCommand prototype bean -->
<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>
@@ -102,8 +102,8 @@ following snippet:
----
<bean id="theTargetBean" class="..." />
<bean id="theClientBean" class="...">
<property name="targetName" ref="theTargetBean"/>
<bean id="client" class="...">
<property name="targetName" value="theTargetBean"/>
</bean>
----
@@ -582,7 +582,7 @@ it needs to be declared in the XML file even though it is not defined in an XSD
(it exists inside the Spring core).
For the rare cases where the constructor argument names are not available (usually if
the bytecode was compiled without the `-parameters` flag), you can fall back to the
the bytecode was compiled without debugging information), you can use fallback to the
argument indexes, as follows:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -439,7 +439,7 @@ dataSource.url=jdbc:mysql:mydb
----
This example file can be used with a container definition that contains a bean called
`dataSource` that has `driverClassName` and `url` properties.
`dataSource` that has `driver` and `url` properties.
Compound property names are also supported, as long as every component of the path
except the final property being overridden is already non-null (presumably initialized
@@ -116,7 +116,7 @@ the configuration model, in that references to other beans must be valid Java sy
Fortunately, solving this problem is simple. As
xref:core/beans/java/bean-annotation.adoc#beans-java-dependencies[we already discussed],
a `@Bean` method can have an arbitrary number of parameters that describe the bean
dependencies. Consider the following more realistic scenario with several `@Configuration`
dependencies. Consider the following more real-world scenario with several `@Configuration`
classes, each depending on beans declared in the others:
[tabs]
@@ -331,10 +331,8 @@ TIP: Constructor injection in `@Configuration` classes is only supported as of S
Framework 4.3. Note also that there is no need to specify `@Autowired` if the target
bean defines only one constructor.
[discrete]
[[beans-java-injecting-imported-beans-fq]]
==== Fully-qualifying imported beans for ease of navigation
.[[beans-java-injecting-imported-beans-fq]]Fully-qualifying imported beans for ease of navigation
--
In the preceding scenario, using `@Autowired` works well and provides the desired
modularity, but determining exactly where the autowired bean definitions are declared is
still somewhat ambiguous. For example, as a developer looking at `ServiceConfig`, how do
@@ -503,11 +501,49 @@ Now `ServiceConfig` is loosely coupled with respect to the concrete
get a type hierarchy of `RepositoryConfig` implementations. In this
way, navigating `@Configuration` classes and their dependencies becomes no different
than the usual process of navigating interface-based code.
--
TIP: If you want to influence the startup creation order of certain beans, consider
declaring some of them as `@Lazy` (for creation on first access instead of on startup)
or as `@DependsOn` certain other beans (making sure that specific other beans are
created before the current bean, beyond what the latter's direct dependencies imply).
[[beans-java-startup]]
== Influencing the Startup of `@Bean`-defined Singletons
If you want to influence the startup creation order of certain singleton beans, consider
declaring some of them as `@Lazy` for creation on first access instead of on startup.
`@DependsOn` forces certain other beans to be initialized first, making sure that
the specified beans are created before the current bean, beyond what the latter's
direct dependencies imply.
[[beans-java-startup-background]]
=== Background Initialization
As of 6.2, there is a background initialization option: `@Bean(bootstrap=BACKGROUND)`
allows for singling out specific beans for background initialization, covering the
entire bean creation step for each such bean on context startup.
Dependent beans with non-lazy injection points automatically wait for the bean instance
to be completed. All regular background initializations are forced to complete at the end
of context startup. Only beans additionally marked as `@Lazy` are allowed to be completed
later (up until the first actual access).
Background initialization typically goes together with `@Lazy` (or `ObjectProvider`)
injection points in dependent beans. Otherwise, the main bootstrap thread is going to
block when an actual background-initialized bean instance needs to be injected early.
This form of concurrent startup applies to individual beans: if such a bean depends on
other beans, they need to have been initialized already, either simply through being
declared earlier or through `@DependsOn` which enforces initialization in the main
bootstrap thread before background initialization for the affected bean is triggered.
[NOTE]
====
A `bootstrapExecutor` bean of type `Executor` must be declared for background
bootstrapping to be actually active. Otherwise, the background markers will be ignored at
runtime.
The bootstrap executor may be a bounded executor just for startup purposes or a shared
thread pool which serves for other purposes as well.
====
[[beans-java-conditional]]
@@ -541,7 +577,7 @@ Java::
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().matchesProfiles((String[]) value)) {
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
return true;
}
}
@@ -560,7 +596,7 @@ Kotlin::
val attrs = metadata.getAllAnnotationAttributes(Profile::class.java.name)
if (attrs != null) {
for (value in attrs["value"]!!) {
if (context.environment.matchesProfiles(*value as Array<String>)) {
if (context.environment.acceptsProfiles(Profiles.of(*value as Array<String>))) {
return true
}
}
@@ -595,18 +631,16 @@ that uses Spring XML, it is easier to create `@Configuration` classes on an
as-needed basis and include them from the existing XML files. Later in this section, we cover the
options for using `@Configuration` classes in this kind of "`XML-centric`" situation.
[discrete]
[[beans-java-combining-xml-centric-declare-as-bean]]
==== Declaring `@Configuration` classes as plain Spring `<bean/>` elements
Remember that `@Configuration` classes are ultimately bean definitions in the container.
In this series of examples, we create a `@Configuration` class named `AppConfig` and
.[[beans-java-combining-xml-centric-declare-as-bean]]Declaring `@Configuration` classes as plain Spring `<bean/>` elements
--
Remember that `@Configuration` classes are ultimately bean definitions in the
container. In this series examples, we create a `@Configuration` class named `AppConfig` and
include it within `system-test-config.xml` as a `<bean/>` definition. Because
`<context:annotation-config/>` is switched on, the container recognizes the
`@Configuration` annotation and processes the `@Bean` methods declared in `AppConfig`
properly.
The following example shows the `AppConfig` configuration class in Java and Kotlin:
The following example shows an ordinary configuration class in Java:
[tabs]
======
@@ -627,7 +661,7 @@ Java::
@Bean
public TransferService transferService() {
return new TransferServiceImpl(accountRepository());
return new TransferService(accountRepository());
}
}
----
@@ -660,7 +694,6 @@ The following example shows part of a sample `system-test-config.xml` file:
<beans>
<!-- enable processing of annotations such as @Autowired and @Configuration -->
<context:annotation-config/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean class="com.acme.AppConfig"/>
@@ -707,20 +740,20 @@ Kotlin::
----
======
NOTE: In the `system-test-config.xml` file, the `AppConfig` `<bean/>` does not declare an `id`
attribute. While it would be acceptable to do so, it is unnecessary, given that no other bean
NOTE: In `system-test-config.xml` file, the `AppConfig` `<bean/>` does not declare an `id`
element. While it would be acceptable to do so, it is unnecessary, given that no other bean
ever refers to it, and it is unlikely to be explicitly fetched from the container by name.
Similarly, the `DataSource` bean is only ever autowired by type, so an explicit bean `id`
is not strictly required.
--
[discrete]
[[beans-java-combining-xml-centric-component-scan]]
==== Using <context:component-scan/> to pick up `@Configuration` classes
.[[beans-java-combining-xml-centric-component-scan]] Using <context:component-scan/> to pick up `@Configuration` classes
--
Because `@Configuration` is meta-annotated with `@Component`, `@Configuration`-annotated
classes are automatically candidates for component scanning. Using the same scenario as
described in the previous example, we can redefine `system-test-config.xml` to take
advantage of component-scanning. Note that, in this case, we need not explicitly declare
described in the previous example, we can redefine `system-test-config.xml` to take advantage of component-scanning.
Note that, in this case, we need not explicitly declare
`<context:annotation-config/>`, because `<context:component-scan/>` enables the same
functionality.
@@ -731,7 +764,6 @@ The following example shows the modified `system-test-config.xml` file:
<beans>
<!-- picks up and registers AppConfig as a bean definition -->
<context:component-scan base-package="com.acme"/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
@@ -741,17 +773,19 @@ The following example shows the modified `system-test-config.xml` file:
</bean>
</beans>
----
--
[[beans-java-combining-java-centric]]
=== `@Configuration` Class-centric Use of XML with `@ImportResource`
In applications where `@Configuration` classes are the primary mechanism for configuring
the container, it may still be necessary to use at least some XML. In such scenarios, you
can use `@ImportResource` and define only as much XML as you need. Doing so achieves a
"`Java-centric`" approach to configuring the container and keeps XML to a bare minimum.
The following example (which includes a configuration class, an XML file that defines a
bean, a properties file, and the `main()` method) shows how to use the `@ImportResource`
annotation to achieve "`Java-centric`" configuration that uses XML as needed:
the container, it is still likely necessary to use at least some XML. In these
scenarios, you can use `@ImportResource` and define only as much XML as you need. Doing
so achieves a "`Java-centric`" approach to configuring the container and keeps XML to a
bare minimum. The following example (which includes a configuration class, an XML file
that defines a bean, a properties file, and the `main` class) shows how to use
the `@ImportResource` annotation to achieve "`Java-centric`" configuration that uses XML
as needed:
[tabs]
======
@@ -776,17 +810,6 @@ Java::
public DataSource dataSource() {
return new DriverManagerDataSource(url, username, password);
}
@Bean
public AccountRepository accountRepository(DataSource dataSource) {
return new JdbcAccountRepository(dataSource);
}
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
----
@@ -811,32 +834,21 @@ Kotlin::
fun dataSource(): DataSource {
return DriverManagerDataSource(url, username, password)
}
@Bean
fun accountRepository(dataSource: DataSource): AccountRepository {
return JdbcAccountRepository(dataSource)
}
@Bean
fun transferService(accountRepository: AccountRepository): TransferService {
return TransferServiceImpl(accountRepository)
}
}
----
======
.properties-config.xml
[source,xml,indent=0,subs="verbatim,quotes"]
----
properties-config.xml
<beans>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>
----
.jdbc.properties
[literal,subs="verbatim,quotes"]
----
jdbc.properties
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
@@ -869,3 +881,5 @@ Kotlin::
----
======
@@ -1,219 +1,34 @@
[[expressions-beandef]]
= Expressions in Bean Definitions
You can use SpEL expressions with XML-based or annotation-based configuration metadata for
defining `BeanDefinition` instances. In both cases, the syntax to define the expression is of the
form `#{ <expression string> }`.
[[expressions-beandef-xml-based]]
== XML Configuration
A property or constructor argument value can be set by using expressions, as the following
example shows:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="numberGuess" class="org.spring.samples.NumberGuess">
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
<!-- other properties -->
</bean>
----
You can use SpEL expressions with configuration metadata for defining bean instances. In both
cases, the syntax to define the expression is of the form `#{ <expression string> }`.
All beans in the application context are available as predefined variables with their
common bean name. This includes standard context beans such as `environment` (of type
`org.springframework.core.env.Environment`) as well as `systemProperties` and
`systemEnvironment` (of type `Map<String, Object>`) for access to the runtime environment.
The following example shows access to the `systemProperties` bean as a SpEL variable:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="taxCalculator" class="org.spring.samples.TaxCalculator">
<property name="defaultLocale" value="#{ systemProperties['user.region'] }"/>
<!-- other properties -->
</bean>
----
Note that you do not have to prefix the predefined variable with the `#` symbol here.
You can also refer to other bean properties by name, as the following example shows:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="numberGuess" class="org.spring.samples.NumberGuess">
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
<!-- other properties -->
</bean>
<bean id="shapeGuess" class="org.spring.samples.ShapeGuess">
<property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/>
<!-- other properties -->
</bean>
----
[[expressions-beandef-annotation-based]]
== Annotation Configuration
To specify a default value, you can place the `@Value` annotation on fields, methods,
and method or constructor parameters.
and method or constructor parameters (or XML equivalent).
The following example sets the default value of a field:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class FieldValueTestBean {
include-code::./FieldValueTestBean[tag=snippet,indent=0]
@Value("#{ systemProperties['user.region'] }")
private String defaultLocale;
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale() {
return this.defaultLocale;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class FieldValueTestBean {
@Value("#{ systemProperties['user.region'] }")
var defaultLocale: String? = null
}
----
======
Note that you do not have to prefix the predefined variable with the `#` symbol here.
The following example shows the equivalent but on a property setter method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class PropertyValueTestBean {
private String defaultLocale;
@Value("#{ systemProperties['user.region'] }")
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale() {
return this.defaultLocale;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class PropertyValueTestBean {
@Value("#{ systemProperties['user.region'] }")
var defaultLocale: String? = null
}
----
======
include-code::./PropertyValueTestBean[tag=snippet,indent=0]
Autowired methods and constructors can also use the `@Value` annotation, as the following
examples show:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
private MovieFinder movieFinder;
private String defaultLocale;
@Autowired
public void configure(MovieFinder movieFinder,
@Value("#{ systemProperties['user.region'] }") String defaultLocale) {
this.movieFinder = movieFinder;
this.defaultLocale = defaultLocale;
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SimpleMovieLister {
private lateinit var movieFinder: MovieFinder
private lateinit var defaultLocale: String
@Autowired
fun configure(movieFinder: MovieFinder,
@Value("#{ systemProperties['user.region'] }") defaultLocale: String) {
this.movieFinder = movieFinder
this.defaultLocale = defaultLocale
}
// ...
}
----
======
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
private String defaultLocale;
private CustomerPreferenceDao customerPreferenceDao;
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao,
@Value("#{systemProperties['user.country']}") String defaultLocale) {
this.customerPreferenceDao = customerPreferenceDao;
this.defaultLocale = defaultLocale;
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender(private val customerPreferenceDao: CustomerPreferenceDao,
@Value("#{systemProperties['user.country']}") private val defaultLocale: String) {
// ...
}
----
======
include-code::./SimpleMovieLister[tag=snippet,indent=0]
include-code::./MovieRecommender[tag=snippet,indent=0]
You can also refer to other bean properties by name, as the following example shows:
include-code::./ShapeGuess[tag=snippet,indent=0]
@@ -222,38 +222,29 @@ Kotlin::
[[expressions-evaluation-context]]
== Understanding `EvaluationContext`
The `EvaluationContext` API is used when evaluating an expression to resolve properties,
methods, or fields and to help perform type conversion. Spring provides two
The `EvaluationContext` interface is used when evaluating an expression to resolve
properties, methods, or fields and to help perform type conversion. Spring provides two
implementations.
`SimpleEvaluationContext`::
Exposes a subset of essential SpEL language features and configuration options, for
categories of expressions that do not require the full extent of the SpEL language
syntax and should be meaningfully restricted. Examples include but are not limited to
data binding expressions and property-based filters.
* `SimpleEvaluationContext`: Exposes a subset of essential SpEL language features and
configuration options, for categories of expressions that do not require the full extent
of the SpEL language syntax and should be meaningfully restricted. Examples include but
are not limited to data binding expressions and property-based filters.
`StandardEvaluationContext`::
Exposes the full set of SpEL language features and configuration options. You can use
it to specify a default root object and to configure every available evaluation-related
strategy.
* `StandardEvaluationContext`: Exposes the full set of SpEL language features and
configuration options. You can use it to specify a default root object and to configure
every available evaluation-related strategy.
`SimpleEvaluationContext` is designed to support only a subset of the SpEL language
syntax. For example, it excludes Java type references, constructors, and bean references.
It also requires you to explicitly choose the level of support for properties and methods
in expressions. When creating a `SimpleEvaluationContext` you need to choose the level of
support that you need for data binding in SpEL expressions:
`SimpleEvaluationContext` is designed to support only a subset of the SpEL language syntax.
It excludes Java type references, constructors, and bean references. It also requires
you to explicitly choose the level of support for properties and methods in expressions.
By default, the `create()` static factory method enables only read access to properties.
You can also obtain a builder to configure the exact level of support needed, targeting
one or some combination of the following.
* Data binding for read-only access
* Data binding for read and write access
* A custom `PropertyAccessor` (typically not reflection-based), potentially combined with
a `DataBindingPropertyAccessor`
Conveniently, `SimpleEvaluationContext.forReadOnlyDataBinding()` enables read-only access
to properties via `DataBindingPropertyAccessor`. Similarly,
`SimpleEvaluationContext.forReadWriteDataBinding()` enables read and write access to
properties. Alternatively, configure custom accessors via
`SimpleEvaluationContext.forPropertyAccessors(...)`, potentially disable assignment, and
optionally activate method resolution and/or a type converter through the builder.
* Custom `PropertyAccessor` only (no reflection)
* Data binding properties for read-only access
* Data binding properties for read and write
[[expressions-type-conversion]]
@@ -323,17 +314,17 @@ Kotlin::
It is possible to configure the SpEL expression parser by using a parser configuration
object (`org.springframework.expression.spel.SpelParserConfiguration`). The configuration
object controls the behavior of some of the expression components. For example, if you
index into a collection and the element at the specified index is `null`, SpEL can
automatically create the element. This is useful when using expressions made up of a
chain of property references. Similarly, if you index into a collection and specify an
index that is greater than the current size of the collection, SpEL can automatically
grow the collection to accommodate that index. In order to add an element at the
index into an array or collection and the element at the specified index is `null`, SpEL
can automatically create the element. This is useful when using expressions made up of a
chain of property references. If you index into an array or list and specify an index
that is beyond the end of the current size of the array or list, SpEL can automatically
grow the array or list to accommodate that index. In order to add an element at the
specified index, SpEL will try to create the element using the element type's default
constructor before setting the specified value. If the element type does not have a
default constructor, `null` will be added to the collection. If there is no built-in
converter or custom converter that knows how to set the value, `null` will remain in the
collection at the specified index. The following example demonstrates how to
automatically grow a `List`.
default constructor, `null` will be added to the array or list. If there is no built-in
or custom converter that knows how to set the value, `null` will remain in the array or
list at the specified index. The following example demonstrates how to automatically grow
the list.
[tabs]
======
@@ -525,7 +516,7 @@ following kinds of expressions cannot be compiled.
* Expressions involving assignment
* Expressions relying on the conversion service
* Expressions using custom resolvers or accessors
* Expressions using custom resolvers
* Expressions using overloaded operators
* Expressions using array construction syntax
* Expressions using selection or projection
@@ -1,7 +1,7 @@
[[expressions-operator-safe-navigation]]
= Safe Navigation Operator
The safe navigation operator (`?`) is used to avoid a `NullPointerException` and comes
The safe navigation operator (`?.`) is used to avoid a `NullPointerException` and comes
from the https://www.groovy-lang.org/operators.html#_safe_navigation_operator[Groovy]
language. Typically, when you have a reference to an object, you might need to verify
that it is not `null` before accessing methods or properties of the object. To avoid
@@ -81,6 +81,65 @@ For example, the expression `#calculator?.max(4, 2)` evaluates to `null` if the
`max(int, int)` method will be invoked on the `#calculator`.
====
[[expressions-operator-safe-navigation-indexing]]
== Safe Index Access
Since Spring Framework 6.2, the Spring Expression Language supports safe navigation for
indexing into the following types of structures.
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-arrays-and-collections[arrays and collections]
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-strings[strings]
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-maps[maps]
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-objects[objects]
* xref:core/expressions/language-ref/properties-arrays.adoc#expressions-indexing-custom[custom]
The following example shows how to use the safe navigation operator for indexing into
a list (`?.[]`).
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ExpressionParser parser = new SpelExpressionParser();
IEEE society = new IEEE();
EvaluationContext context = new StandardEvaluationContext(society);
// evaluates to Inventor("Nikola Tesla")
Inventor inventor = parser.parseExpression("members?.[0]") // <1>
.getValue(context, Inventor.class);
society.members = null;
// evaluates to null - does not throw an exception
inventor = parser.parseExpression("members?.[0]") // <2>
.getValue(context, Inventor.class);
----
<1> Use null-safe index operator on a non-null `members` list
<2> Use null-safe index operator on a null `members` list
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val parser = SpelExpressionParser()
val society = IEEE()
val context = StandardEvaluationContext(society)
// evaluates to Inventor("Nikola Tesla")
var inventor = parser.parseExpression("members?.[0]") // <1>
.getValue(context, Inventor::class.java)
society.members = null
// evaluates to null - does not throw an exception
inventor = parser.parseExpression("members?.[0]") // <2>
.getValue(context, Inventor::class.java)
----
<1> Use null-safe index operator on a non-null `members` list
<2> Use null-safe index operator on a null `members` list
======
[[expressions-operator-safe-navigation-selection-and-projection]]
== Safe Collection Selection and Projection
@@ -1,11 +1,25 @@
[[expressions-properties-arrays]]
= Properties, Arrays, Lists, Maps, and Indexers
Navigating with property references is easy. To do so, use a period to indicate a nested
property value. The instances of the `Inventor` class, `pupin` and `tesla`, were
populated with data listed in the xref:core/expressions/example-classes.adoc[Classes used in the examples]
section. To navigate "down" the object graph and get Tesla's year of birth and
Pupin's city of birth, we use the following expressions:
The Spring Expression Language provides support for navigating object graphs and indexing
into various structures.
NOTE: Numerical index values are zero-based, such as when accessing the n^th^ element of
an array in Java.
TIP: See the xref:core/expressions/language-ref/operator-safe-navigation.adoc[Safe Navigation Operator]
section for details on how to navigate object graphs and index into various structures
using the null-safe operator.
[[expressions-property-navigation]]
== Property Navigation
You can navigate property references within an object graph by using a period to indicate
a nested property value. The instances of the `Inventor` class, `pupin` and `tesla`, were
populated with data listed in the
xref:core/expressions/example-classes.adoc[Classes used in the examples] section. To
navigate _down_ the object graph and get Tesla's year of birth and Pupin's city of birth,
we use the following expressions:
[tabs]
======
@@ -16,6 +30,7 @@ Java::
// evaluates to 1856
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);
// evaluates to "Smiljan"
String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);
----
@@ -26,6 +41,7 @@ Kotlin::
// evaluates to 1856
val year = parser.parseExpression("birthdate.year + 1900").getValue(context) as Int
// evaluates to "Smiljan"
val city = parser.parseExpression("placeOfBirth.city").getValue(context) as String
----
======
@@ -39,8 +55,20 @@ method invocations -- for example, `getPlaceOfBirth().getCity()` instead of
`placeOfBirth.city`.
====
The contents of arrays and lists are obtained by using square bracket notation, as the
following example shows:
[[expressions-indexing-arrays-and-collections]]
== Indexing into Arrays and Collections
The n^th^ element of an array or collection (for example, a `Set` or `List`) can be
obtained by using square bracket notation, as the following example shows.
[NOTE]
====
If the indexed collection is a `java.util.List`, the n^th^ element will be accessed
directly via `list.get(n)`.
For any other type of `Collection`, the n^th^ element will be accessed by iterating over
the collection using its `Iterator` and returning the n^th^ element encountered.
====
[tabs]
======
@@ -63,7 +91,8 @@ Java::
String name = parser.parseExpression("members[0].name").getValue(
context, ieee, String.class);
// List and Array navigation
// List and Array Indexing
// evaluates to "Wireless communication"
String invention = parser.parseExpression("members[0].inventions[6]").getValue(
context, ieee, String.class);
@@ -88,16 +117,22 @@ Kotlin::
val name = parser.parseExpression("members[0].name").getValue(
context, ieee, String::class.java)
// List and Array navigation
// List and Array Indexing
// evaluates to "Wireless communication"
val invention = parser.parseExpression("members[0].inventions[6]").getValue(
context, ieee, String::class.java)
----
======
The contents of maps are obtained by specifying the literal key value within the
brackets. In the following example, because keys for the `officers` map are strings, we can specify
string literals:
[[expressions-indexing-strings]]
== Indexing into Strings
The n^th^ character of a string can be obtained by specifying the index within square
brackets, as demonstrated in the following example.
NOTE: The n^th^ character of a string will evaluate to a `java.lang.String`, not a
`java.lang.Character`.
[tabs]
======
@@ -105,38 +140,225 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// Officer's Dictionary
Inventor pupin = parser.parseExpression("officers['president']").getValue(
societyContext, Inventor.class);
// evaluates to "Idvor"
String city = parser.parseExpression("officers['president'].placeOfBirth.city").getValue(
societyContext, String.class);
// setting values
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
societyContext, "Croatia");
// evaluates to "T" (8th letter of "Nikola Tesla")
String character = parser.parseExpression("members[0].name[7]")
.getValue(societyContext, String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Officer's Dictionary
val pupin = parser.parseExpression("officers['president']").getValue(
societyContext, Inventor::class.java)
// evaluates to "Idvor"
val city = parser.parseExpression("officers['president'].placeOfBirth.city").getValue(
societyContext, String::class.java)
// setting values
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
societyContext, "Croatia")
// evaluates to "T" (8th letter of "Nikola Tesla")
val character = parser.parseExpression("members[0].name[7]")
.getValue(societyContext, String::class.java)
----
======
[[expressions-indexing-maps]]
== Indexing into Maps
The contents of maps are obtained by specifying the key value within square brackets. In
the following example, because keys for the `officers` map are strings, we can specify
string literals such as `'president'`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// Officer's Map
// evaluates to Inventor("Pupin")
Inventor pupin = parser.parseExpression("officers['president']")
.getValue(societyContext, Inventor.class);
// evaluates to "Idvor"
String city = parser.parseExpression("officers['president'].placeOfBirth.city")
.getValue(societyContext, String.class);
String countryExpression = "officers['advisors'][0].placeOfBirth.country";
// setting values
parser.parseExpression(countryExpression)
.setValue(societyContext, "Croatia");
// evaluates to "Croatia"
String country = parser.parseExpression(countryExpression)
.getValue(societyContext, String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Officer's Map
// evaluates to Inventor("Pupin")
val pupin = parser.parseExpression("officers['president']")
.getValue(societyContext, Inventor::class.java)
// evaluates to "Idvor"
val city = parser.parseExpression("officers['president'].placeOfBirth.city")
.getValue(societyContext, String::class.java)
val countryExpression = "officers['advisors'][0].placeOfBirth.country"
// setting values
parser.parseExpression(countryExpression)
.setValue(societyContext, "Croatia")
// evaluates to "Croatia"
val country = parser.parseExpression(countryExpression)
.getValue(societyContext, String::class.java)
----
======
[[expressions-indexing-objects]]
== Indexing into Objects
A property of an object can be obtained by specifying the name of the property within
square brackets. This is analogous to accessing the value of a map based on its key. The
following example demonstrates how to _index_ into an object to retrieve a specific
property.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// Create an inventor to use as the root context object.
Inventor tesla = new Inventor("Nikola Tesla");
// evaluates to "Nikola Tesla"
String name = parser.parseExpression("#root['name']")
.getValue(context, tesla, String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Create an inventor to use as the root context object.
val tesla = Inventor("Nikola Tesla")
// evaluates to "Nikola Tesla"
val name = parser.parseExpression("#root['name']")
.getValue(context, tesla, String::class.java)
----
======
[[expressions-indexing-custom]]
== Indexing into Custom Structures
Since Spring Framework 6.2, the Spring Expression Language supports indexing into custom
structures by allowing developers to implement and register an `IndexAccessor` with the
`EvaluationContext`. If you would like to support
xref:core/expressions/evaluation.adoc#expressions-spel-compilation[compilation] of
expressions that rely on a custom index accessor, that index accessor must implement the
`CompilableIndexAccessor` SPI.
To support common use cases, Spring provides a built-in `ReflectiveIndexAccessor` which
is a flexible `IndexAccessor` that uses reflection to read from and optionally write to
an indexed structure of a target object. The indexed structure can be accessed through a
`public` read-method (when being read) or a `public` write-method (when being written).
The relationship between the read-method and write-method is based on a convention that
is applicable for typical implementations of indexed structures.
NOTE: `ReflectiveIndexAccessor` also implements `CompilableIndexAccessor` in order to
support xref:core/expressions/evaluation.adoc#expressions-spel-compilation[compilation]
to bytecode for read access. Note, however, that the configured read-method must be
invokable via a `public` class or `public` interface for compilation to succeed.
The following code listings define a `Color` enum and `FruitMap` type that behaves like a
map but does not implement the `java.util.Map` interface. Thus, if you want to index into
a `FruitMap` within a SpEL expression, you will need to register an `IndexAccessor`.
[source,java,indent=0,subs="verbatim,quotes"]
----
package example;
public enum Color {
RED, ORANGE, YELLOW
}
----
[source,java,indent=0,subs="verbatim,quotes"]
----
public class FruitMap {
private final Map<Color, String> map = new HashMap<>();
public FruitMap() {
this.map.put(Color.RED, "cherry");
this.map.put(Color.ORANGE, "orange");
this.map.put(Color.YELLOW, "banana");
}
public String getFruit(Color color) {
return this.map.get(color);
}
public void setFruit(Color color, String fruit) {
this.map.put(color, fruit);
}
}
----
A read-only `IndexAccessor` for `FruitMap` can be created via `new
ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit")`. With that accessor
registered and a `FruitMap` registered as a variable named `#fruitMap`, the SpEL
expression `#fruitMap[T(example.Color).RED]` will evaluate to `"cherry"`.
A read-write `IndexAccessor` for `FruitMap` can be created via `new
ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit", "setFruit")`. With that
accessor registered and a `FruitMap` registered as a variable named `#fruitMap`, the SpEL
expression `#fruitMap[T(example.Color).RED] = 'strawberry'` can be used to change the
fruit mapping for the color red from `"cherry"` to `"strawberry"`.
The following example demonstrates how to register a `ReflectiveIndexAccessor` to index
into a `FruitMap` and then index into the `FruitMap` within a SpEL expression.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// Create a ReflectiveIndexAccessor for FruitMap
IndexAccessor fruitMapAccessor = new ReflectiveIndexAccessor(
FruitMap.class, Color.class, "getFruit", "setFruit");
// Register the IndexAccessor for FruitMap
context.addIndexAccessor(fruitMapAccessor);
// Register the fruitMap variable
context.setVariable("fruitMap", new FruitMap());
// evaluates to "cherry"
String fruit = parser.parseExpression("#fruitMap[T(example.Color).RED]")
.getValue(context, String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Create a ReflectiveIndexAccessor for FruitMap
val fruitMapAccessor = ReflectiveIndexAccessor(
FruitMap::class.java, Color::class.java, "getFruit", "setFruit")
// Register the IndexAccessor for FruitMap
context.addIndexAccessor(fruitMapAccessor)
// Register the fruitMap variable
context.setVariable("fruitMap", FruitMap())
// evaluates to "cherry"
val fruit = parser.parseExpression("#fruitMap[T(example.Color).RED]")
.getValue(context, String::class.java)
----
======
@@ -3,7 +3,7 @@
Expression templates allow mixing literal text with one or more evaluation blocks.
Each evaluation block is delimited with prefix and suffix characters that you can
define. A common choice is to use `#{ }` as the delimiters, as the following example
define. A common choice is to use `+#{ }+` as the delimiters, as the following example
shows:
[tabs]
@@ -32,7 +32,7 @@ Kotlin::
======
The string is evaluated by concatenating the literal text `'random number is '` with the
result of evaluating the expression inside the `#{ }` delimiters (in this case, the
result of evaluating the expression inside the `+#{ }+` delimiters (in this case, the
result of calling that `random()` method). The second argument to the `parseExpression()`
method is of the type `ParserContext`. The `ParserContext` interface is used to influence
how the expression is parsed in order to support the expression templating functionality.
@@ -196,7 +196,7 @@ Kotlin::
When used as `org.springframework.validation.Validator`, `LocalValidatorFactoryBean`
invokes the underlying `jakarta.validation.Validator`, and then adapts
``ConstraintViolation``s to ``FieldError``s, and registers them with the `Errors` object
``ContraintViolation``s to ``FieldError``s, and registers them with the `Errors` object
passed into the `validate` method.
@@ -299,7 +299,7 @@ Java::
public class AppConfig {
@Bean
public static MethodValidationPostProcessor validationPostProcessor() {
public MethodValidationPostProcessor validationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
@@ -341,7 +341,7 @@ xref:web/webflux/ann-rest-exceptions.adoc[Error Responses] sections.
=== Method Validation Exceptions
By default, `jakarta.validation.ConstraintViolationException` is raised with the set of
``ConstraintViolation``s returned by `jakarta.validation.Validator`. As an alternative,
``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:
@@ -357,7 +357,7 @@ Java::
public class AppConfig {
@Bean
public static MethodValidationPostProcessor validationPostProcessor() {
public MethodValidationPostProcessor validationPostProcessor() {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setAdaptConstraintViolations(true);
return processor;
@@ -431,22 +431,22 @@ Kotlin::
A `ConstraintViolation` on `Person.name()` is adapted to a `FieldError` with the following:
- Error codes `"Size.person.name"`, `"Size.name"`, `"Size.java.lang.String"`, and `"Size"`
- 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
`"person.name"` and `"name"` and can customized too. For example:
message argument `"name"` is itself a `MessageSourceResolvable` with error codes
`"student.name"` and `"name"` and can customized too. For example:
Properties::
+
[source,properties,indent=0,subs="verbatim,quotes",role="secondary"]
----
Size.person.name=Please, provide a {0} that is between {2} and {1} characters long
person.name=username
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
@@ -11,106 +11,9 @@ formatters manually with the help of:
* `org.springframework.format.datetime.standard.DateTimeFormatterRegistrar`
* `org.springframework.format.datetime.DateFormatterRegistrar`
For example, the following Java configuration registers a global `yyyyMMdd` format:
For example, the following configuration registers a global `yyyyMMdd` format:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class AppConfig {
@Bean
public FormattingConversionService conversionService() {
// Use the DefaultFormattingConversionService but do not register defaults
DefaultFormattingConversionService conversionService =
new DefaultFormattingConversionService(false);
// Ensure @NumberFormat is still supported
conversionService.addFormatterForFieldAnnotation(
new NumberFormatAnnotationFormatterFactory());
// Register JSR-310 date conversion with a specific global format
DateTimeFormatterRegistrar dateTimeRegistrar = new DateTimeFormatterRegistrar();
dateTimeRegistrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"));
dateTimeRegistrar.registerFormatters(conversionService);
// Register date conversion with a specific global format
DateFormatterRegistrar dateRegistrar = new DateFormatterRegistrar();
dateRegistrar.setFormatter(new DateFormatter("yyyyMMdd"));
dateRegistrar.registerFormatters(conversionService);
return conversionService;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class AppConfig {
@Bean
fun conversionService(): FormattingConversionService {
// Use the DefaultFormattingConversionService but do not register defaults
return DefaultFormattingConversionService(false).apply {
// Ensure @NumberFormat is still supported
addFormatterForFieldAnnotation(NumberFormatAnnotationFormatterFactory())
// Register JSR-310 date conversion with a specific global format
val dateTimeRegistrar = DateTimeFormatterRegistrar()
dateTimeRegistrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"))
dateTimeRegistrar.registerFormatters(this)
// Register date conversion with a specific global format
val dateRegistrar = DateFormatterRegistrar()
dateRegistrar.setFormatter(DateFormatter("yyyyMMdd"))
dateRegistrar.registerFormatters(this)
}
}
}
----
======
If you prefer XML-based configuration, you can use a
`FormattingConversionServiceFactoryBean`. The following example shows how to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="registerDefaultFormatters" value="false" />
<property name="formatters">
<set>
<bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory" />
</set>
</property>
<property name="formatterRegistrars">
<set>
<bean class="org.springframework.format.datetime.standard.DateTimeFormatterRegistrar">
<property name="dateFormatter">
<bean class="org.springframework.format.datetime.standard.DateTimeFormatterFactoryBean">
<property name="pattern" value="yyyyMMdd"/>
</bean>
</property>
</bean>
</set>
</property>
</bean>
</beans>
----
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
Note there are extra considerations when configuring date and time formats in web
applications. Please see
@@ -280,11 +280,10 @@ Kotlin::
A portable format annotation API exists in the `org.springframework.format.annotation`
package. You can use `@NumberFormat` to format `Number` fields such as `Double` and
`Long`, and `@DateTimeFormat` to format fields such as `java.util.Date`,
`java.util.Calendar`, and `Long` (for millisecond timestamps) as well as JSR-310
`java.time` types.
`Long`, and `@DateTimeFormat` to format `java.util.Date`, `java.util.Calendar`, `Long`
(for millisecond timestamps) as well as JSR-310 `java.time`.
The following example uses `@DateTimeFormat` to format a `java.util.Date` as an ISO date
The following example uses `@DateTimeFormat` to format a `java.util.Date` as an ISO Date
(yyyy-MM-dd):
[tabs]
@@ -310,28 +309,6 @@ Kotlin::
----
======
For further details, see the javadoc for
{spring-framework-api}/format/annotation/DateTimeFormat.html[`@DateTimeFormat`] and
{spring-framework-api}/format/annotation/NumberFormat.html[`@NumberFormat`].
[WARNING]
====
Style-based formatting and parsing rely on locale-sensitive patterns which may change
depending on the Java runtime. Specifically, applications that rely on date, time, or
number parsing and formatting may encounter incompatible changes in behavior when running
on JDK 20 or higher.
Using an ISO standardized format or a concrete pattern that you control allows for
reliable system-independent and locale-independent parsing and formatting of date, time,
and number values.
For `@DateTimeFormat`, the use of fallback patterns can also help to address
compatibility issues.
For further details, see the
https://github.com/spring-projects/spring-framework/wiki/Date-and-Time-Formatting-with-JDK-20-and-higher[Date and Time Formatting with JDK 20 and higher]
page in the Spring Framework wiki.
====
[[format-FormatterRegistry-SPI]]
== The `FormatterRegistry` SPI
@@ -198,7 +198,7 @@ methods it offers can be found in the {spring-framework-api}/validation/Errors.h
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()`
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)`).
@@ -211,28 +211,19 @@ the JDBC driver. If the count is not available, the JDBC driver returns a value
====
In such a scenario, with automatic setting of values on an underlying `PreparedStatement`,
the corresponding JDBC type for each value needs to be derived from the given Java type.
While this usually works well, there is a potential for issues (for example, with
Map-contained `null` values). Spring, by default, calls `ParameterMetaData.getParameterType`
in such a case, which can be expensive with your JDBC driver. You should use a recent driver
While this usually works well, there is a potential for issues (for example, with Map-contained
`null` values). Spring, by default, calls `ParameterMetaData.getParameterType` in such a
case, which can be expensive with your JDBC driver. You should use a recent driver
version and consider setting the `spring.jdbc.getParameterType.ignore` property to `true`
(as a JVM system property or via the
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism)
if you encounter a specific performance issue for your application.
xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism) if you encounter
a performance issue (as reported on Oracle 12c, JBoss, and PostgreSQL).
As of 6.1.2, Spring bypasses the default `getParameterType` resolution on PostgreSQL and
MS SQL Server. This is a common optimization to avoid further roundtrips to the DBMS just
for parameter type resolution which is known to make a very significant difference on
PostgreSQL and MS SQL Server specifically, in particular for batch operations. If you
happen to see a side effect e.g. when setting a byte array to null without specific type
indication, you may explicitly set the `spring.jdbc.getParameterType.ignore=false` flag
as a system property (see above) to restore full `getParameterType` resolution.
Alternatively, you could consider specifying the corresponding JDBC types explicitly,
either through a `BatchPreparedStatementSetter` (as shown earlier), through an explicit
type array given to a `List<Object[]>` based call, through `registerSqlType` calls on a
custom `MapSqlParameterSource` instance, through a `BeanPropertySqlParameterSource`
that derives the SQL type from the Java-declared property type even for a null value, or
through providing individual `SqlParameterValue` instances instead of plain null values.
Alternatively, you might consider specifying the corresponding JDBC types explicitly,
either through a `BatchPreparedStatementSetter` (as shown earlier), through an explicit type
array given to a `List<Object[]>` based call, through `registerSqlType` calls on a
custom `MapSqlParameterSource` instance, or through a `BeanPropertySqlParameterSource`
that derives the SQL type from the Java-declared property type even for a null value.
====
@@ -46,47 +46,9 @@ To configure a `DriverManagerDataSource`:
for the correct value.)
. Provide a username and a password to connect to the database.
The following example shows how to configure a `DriverManagerDataSource` in Java:
The following example shows how to configure a `DriverManagerDataSource`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val dataSource = DriverManagerDataSource().apply {
setDriverClassName("org.hsqldb.jdbcDriver")
url = "jdbc:hsqldb:hsql://localhost:"
username = "sa"
password = ""
}
----
======
The following example shows the corresponding XML configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
----
include-code::./DriverManagerDataSourceConfiguration[tag=snippet,indent=0]
The next two examples show the basic connectivity and configuration for DBCP and C3P0.
To learn about more options that help control the pooling features, see the product
@@ -94,32 +56,11 @@ documentation for the respective connection pooling implementations.
The following example shows DBCP configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
----
include-code::./BasicDataSourceConfiguration[tag=snippet,indent=0]
The following example shows C3P0 configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
----
include-code::./ComboPooledDataSourceConfiguration[tag=snippet,indent=0]
[[jdbc-DataSourceUtils]]
== Using `DataSourceUtils`
@@ -339,7 +339,7 @@ Kotlin::
More sophisticated stored procedure support is xref:data-access/jdbc/object.adoc#jdbc-StoredProcedure[covered later].
[[jdbc-JdbcTemplate-idioms]]
[[jdbc-jdbctemplate-idioms]]
=== `JdbcTemplate` Best Practices
Instances of the `JdbcTemplate` class are thread-safe, once configured. This is
@@ -352,147 +352,23 @@ A common practice when using the `JdbcTemplate` class (and the associated
xref:data-access/jdbc/core.adoc#jdbc-NamedParameterJdbcTemplate[`NamedParameterJdbcTemplate`] class) is to
configure a `DataSource` in your Spring configuration file and then dependency-inject
that shared `DataSource` bean into your DAO classes. The `JdbcTemplate` is created in
the setter for the `DataSource`. This leads to DAOs that resemble the following:
the setter for the `DataSource` or in the constructor. This leads to DAOs that resemble the following:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class JdbcCorporateEventDao implements CorporateEventDao {
include-code::./JdbcCorporateEventDao[tag=snippet,indent=0]
private JdbcTemplate jdbcTemplate;
The following example shows the corresponding configuration:
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class JdbcCorporateEventDao(dataSource: DataSource) : CorporateEventDao {
private val jdbcTemplate = JdbcTemplate(dataSource)
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
======
--
The following example shows the corresponding XML configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
</beans>
----
include-code::./JdbcCorporateEventDaoConfiguration[tag=snippet,indent=0]
An alternative to explicit configuration is to use component-scanning and annotation
support for dependency injection. In this case, you can annotate the class with `@Repository`
(which makes it a candidate for component-scanning) and annotate the `DataSource` setter
method with `@Autowired`. The following example shows how to do so:
(which makes it a candidate for component-scanning). The following example shows how to do so:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Repository // <1>
public class JdbcCorporateEventDao implements CorporateEventDao {
include-code::./JdbcCorporateEventRepository[tag=snippet,indent=0]
private JdbcTemplate jdbcTemplate;
The following example shows the corresponding configuration:
@Autowired // <2>
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource); // <3>
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
<1> Annotate the class with `@Repository`.
<2> Annotate the `DataSource` setter method with `@Autowired`.
<3> Create a new `JdbcTemplate` with the `DataSource`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Repository // <1>
class JdbcCorporateEventDao(dataSource: DataSource) : CorporateEventDao { // <2>
private val jdbcTemplate = JdbcTemplate(dataSource) // <3>
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
----
<1> Annotate the class with `@Repository`.
<2> Constructor injection of the `DataSource`.
<3> Create a new `JdbcTemplate` with the `DataSource`.
======
--
The following example shows the corresponding XML configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- Scans within the base package of the application for @Component classes to configure as beans -->
<context:component-scan base-package="org.springframework.docs.test" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
</beans>
----
include-code::./JdbcCorporateEventRepositoryConfiguration[tag=snippet,indent=0]
If you use Spring's `JdbcDaoSupport` class and your various JDBC-backed DAO classes
extend from it, your sub-class inherits a `setDataSource(..)` method from the
@@ -698,7 +574,7 @@ functionality that is present only in the `JdbcTemplate` class, you can use the
`getJdbcOperations()` method to access the wrapped `JdbcTemplate` through the
`JdbcOperations` interface.
See also xref:data-access/jdbc/core.adoc#jdbc-JdbcTemplate-idioms[`JdbcTemplate` Best Practices]
See also xref:data-access/jdbc/core.adoc#jdbc-jdbctemplate-idioms[`JdbcTemplate` Best Practices]
for guidelines on using the `NamedParameterJdbcTemplate` class in the context of an application.
@@ -16,124 +16,22 @@ lightweight nature. Benefits include ease of configuration, quick startup time,
testability, and the ability to rapidly evolve your SQL during development.
[[jdbc-embedded-database-xml]]
== Creating an Embedded Database by Using Spring XML
[[jdbc-embedded-database]]
== Creating an Embedded Database
If you want to expose an embedded database instance as a bean in a Spring
`ApplicationContext`, you can use the `embedded-database` tag in the `spring-jdbc` namespace:
You can expose an embedded database instance as a bean as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jdbc:embedded-database id="dataSource" generate-name="true">
<jdbc:script location="classpath:schema.sql"/>
<jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>
----
include-code::./JdbcEmbeddedDatabaseConfiguration[tag=snippet,indent=0]
The preceding configuration creates an embedded HSQL database that is populated with SQL from
The preceding configuration creates an embedded H2 database that is populated with SQL from
the `schema.sql` and `test-data.sql` resources in the root of the classpath. In addition, as
a best practice, the embedded database is assigned a uniquely generated name. The
embedded database is made available to the Spring container as a bean of type
`javax.sql.DataSource` that can then be injected into data access objects as needed.
[[jdbc-embedded-database-java]]
== Creating an Embedded Database Programmatically
The `EmbeddedDatabaseBuilder` class provides a fluent API for constructing an embedded
database programmatically. You can use this when you need to create an embedded database in a
stand-alone environment or in a stand-alone integration test, as in the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(H2)
.setScriptEncoding("UTF-8")
.ignoreFailedDrops(true)
.addScript("schema.sql")
.addScripts("user_data.sql", "country_data.sql")
.build();
// perform actions against the db (EmbeddedDatabase extends javax.sql.DataSource)
db.shutdown()
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val db = EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(H2)
.setScriptEncoding("UTF-8")
.ignoreFailedDrops(true)
.addScript("schema.sql")
.addScripts("user_data.sql", "country_data.sql")
.build()
// perform actions against the db (EmbeddedDatabase extends javax.sql.DataSource)
db.shutdown()
----
======
See the {spring-framework-api}/jdbc/datasource/embedded/EmbeddedDatabaseBuilder.html[javadoc for `EmbeddedDatabaseBuilder`]
for further details on all supported options.
You can also use the `EmbeddedDatabaseBuilder` to create an embedded database by using Java
configuration, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(H2)
.setScriptEncoding("UTF-8")
.ignoreFailedDrops(true)
.addScript("schema.sql")
.addScripts("user_data.sql", "country_data.sql")
.build();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class DataSourceConfig {
@Bean
fun dataSource(): DataSource {
return EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(H2)
.setScriptEncoding("UTF-8")
.ignoreFailedDrops(true)
.addScript("schema.sql")
.addScripts("user_data.sql", "country_data.sql")
.build()
}
}
----
======
[[jdbc-embedded-database-types]]
== Selecting the Embedded Database Type
@@ -168,6 +66,74 @@ attribute of the `embedded-database` tag to `DERBY`. If you use the builder API,
call the `setType(EmbeddedDatabaseType)` method with `EmbeddedDatabaseType.DERBY`.
[[jdbc-embedded-database-types-custom]]
== Customizing the Embedded Database Type
While each supported type comes with default connection settings, it is possible
to customize them if necessary. The following example uses H2 with a custom driver:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setDatabaseConfigurer(EmbeddedDatabaseConfigurers
.customizeConfigurer(H2, this::customize))
.addScript("schema.sql")
.build();
}
private EmbeddedDatabaseConfigurer customize(EmbeddedDatabaseConfigurer defaultConfigurer) {
return new EmbeddedDatabaseConfigurerDelegate(defaultConfigurer) {
@Override
public void configureConnectionProperties(ConnectionProperties properties, String databaseName) {
super.configureConnectionProperties(properties, databaseName);
properties.setDriverClass(CustomDriver.class);
}
};
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class DataSourceConfig {
@Bean
fun dataSource(): DataSource {
return EmbeddedDatabaseBuilder()
.setDatabaseConfigurer(EmbeddedDatabaseConfigurers
.customizeConfigurer(EmbeddedDatabaseType.H2) { this.customize(it) })
.addScript("schema.sql")
.build()
}
private fun customize(defaultConfigurer: EmbeddedDatabaseConfigurer): EmbeddedDatabaseConfigurer {
return object : EmbeddedDatabaseConfigurerDelegate(defaultConfigurer) {
override fun configureConnectionProperties(
properties: ConnectionProperties,
databaseName: String
) {
super.configureConnectionProperties(properties, databaseName)
properties.setDriverClass(CustomDriver::class.java)
}
}
}
}
----
======
[[jdbc-embedded-database-dao-testing]]
== Testing Data Access Logic with an Embedded Database
@@ -177,8 +143,8 @@ can be useful for one-offs when the embedded database does not need to be reused
classes. However, if you wish to create an embedded database that is shared within a test suite,
consider using the xref:testing/testcontext-framework.adoc[Spring TestContext Framework] and
configuring the embedded database as a bean in the Spring `ApplicationContext` as described
in xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-xml[Creating an Embedded Database by Using Spring XML] and xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database-java[Creating an Embedded Database Programmatically]. The following listing
shows the test template:
in xref:data-access/jdbc/embedded-database-support.adoc#jdbc-embedded-database[Creating an Embedded Database].
The following listing shows the test template:
[tabs]
======
@@ -112,7 +112,7 @@ Java::
this.actorMappingQuery = new ActorMappingQuery(dataSource);
}
public Actor getActor(Long id) {
public Customer getCustomer(Long id) {
return actorMappingQuery.findObject(id);
}
----
@@ -123,11 +123,11 @@ Kotlin::
----
private val actorMappingQuery = ActorMappingQuery(dataSource)
fun getActor(id: Long) = actorMappingQuery.findObject(id)
fun getCustomer(id: Long) = actorMappingQuery.findObject(id)
----
======
The method in the preceding example retrieves the actor with the `id` that is passed in as the
The method in the preceding example retrieves the customer with the `id` that is passed in as the
only parameter. Since we want only one object to be returned, we call the `findObject` convenience
method with the `id` as the parameter. If we had instead a query that returned a
list of objects and took additional parameters, we would use one of the `execute`
@@ -209,26 +209,141 @@ are passed in as a parameter to the stored procedure.
The `SqlReturnType` interface has a single method (named `getTypeValue`) that must be
implemented. This interface is used as part of the declaration of an `SqlOutParameter`.
The following example shows returning the value of a `java.sql.Struct` object of the user
The following example shows returning the value of an Oracle `STRUCT` object of the user
declared type `ITEM_TYPE`:
include-code::./TestItemStoredProcedure[]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class TestItemStoredProcedure extends StoredProcedure {
public TestItemStoredProcedure(DataSource dataSource) {
// ...
declareParameter(new SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE",
(CallableStatement cs, int colIndx, int sqlType, String typeName) -> {
STRUCT struct = (STRUCT) cs.getObject(colIndx);
Object[] attr = struct.getAttributes();
TestItem item = new TestItem();
item.setId(((Number) attr[0]).longValue());
item.setDescription((String) attr[1]);
item.setExpirationDate((java.util.Date) attr[2]);
return item;
}));
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() {
init {
// ...
declareParameter(SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE") { cs, colIndx, sqlType, typeName ->
val struct = cs.getObject(colIndx) as STRUCT
val attr = struct.getAttributes()
TestItem((attr[0] as Long, attr[1] as String, attr[2] as Date)
})
// ...
}
}
----
======
You can use `SqlTypeValue` to pass the value of a Java object (such as `TestItem`) to a
stored procedure. The `SqlTypeValue` interface has a single method (named
`createTypeValue`) that you must implement. The active connection is passed in, and you
can use it to create database-specific objects, such as `java.sql.Struct` instances
or `java.sql.Array` instances. The following example creates a `java.sql.Struct` instance:
can use it to create database-specific objects, such as `StructDescriptor` instances
or `ArrayDescriptor` instances. The following example creates a `StructDescriptor` instance:
include-code::./SqlTypeValueFactory[tag=struct,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
final TestItem testItem = new TestItem(123L, "A test item",
new SimpleDateFormat("yyyy-M-d").parse("2010-12-31"));
SqlTypeValue value = new AbstractSqlTypeValue() {
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
StructDescriptor itemDescriptor = new StructDescriptor(typeName, conn);
Struct item = new STRUCT(itemDescriptor, conn,
new Object[] {
testItem.getId(),
testItem.getDescription(),
new java.sql.Date(testItem.getExpirationDate().getTime())
});
return item;
}
};
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val (id, description, expirationDate) = TestItem(123L, "A test item",
SimpleDateFormat("yyyy-M-d").parse("2010-12-31"))
val value = object : AbstractSqlTypeValue() {
override fun createTypeValue(conn: Connection, sqlType: Int, typeName: String?): Any {
val itemDescriptor = StructDescriptor(typeName, conn)
return STRUCT(itemDescriptor, conn,
arrayOf(id, description, java.sql.Date(expirationDate.time)))
}
}
----
======
You can now add this `SqlTypeValue` to the `Map` that contains the input parameters for the
`execute` call of the stored procedure.
Another use for the `SqlTypeValue` is passing in an array of values to an Oracle stored
procedure. Oracle has an `createOracleArray` method on `OracleConnection` that you can
access by unwrapping it. You can use the `SqlTypeValue` to create an array and populate
it with values from the Java `java.sql.Array`, as the following example shows:
procedure. Oracle has its own internal `ARRAY` class that must be used in this case, and
you can use the `SqlTypeValue` to create an instance of the Oracle `ARRAY` and populate
it with values from the Java `ARRAY`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
final Long[] ids = new Long[] {1L, 2L};
SqlTypeValue value = new AbstractSqlTypeValue() {
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
ArrayDescriptor arrayDescriptor = new ArrayDescriptor(typeName, conn);
ARRAY idArray = new ARRAY(arrayDescriptor, conn, ids);
return idArray;
}
};
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() {
init {
val ids = arrayOf(1L, 2L)
val value = object : AbstractSqlTypeValue() {
override fun createTypeValue(conn: Connection, sqlType: Int, typeName: String?): Any {
val arrayDescriptor = ArrayDescriptor(typeName, conn)
return ARRAY(arrayDescriptor, conn, ids)
}
}
}
}
----
======
include-code::./SqlTypeValueFactory[tag=oracle-array,indent=0]
@@ -323,12 +323,12 @@ use these alternative input classes.
The `SimpleJdbcCall` class uses metadata in the database to look up names of `in`
and `out` parameters so that you do not have to explicitly declare them. You can
declare parameters if you prefer to do that or if you have parameters that do not
have an automatic mapping to a Java class. The first example shows a simple procedure
that returns only scalar values in `VARCHAR` and `DATE` format from a MySQL database.
The example procedure reads a specified actor entry and returns `first_name`,
`last_name`, and `birth_date` columns in the form of `out` parameters. The following
listing shows the first example:
declare parameters if you prefer to do that or if you have parameters (such as `ARRAY`
or `STRUCT`) that do not have an automatic mapping to a Java class. The first example
shows a simple procedure that returns only scalar values in `VARCHAR` and `DATE` format
from a MySQL database. The example procedure reads a specified actor entry and returns
`first_name`, `last_name`, and `birth_date` columns in the form of `out` parameters.
The following listing shows the first example:
[source,sql,indent=0,subs="verbatim,quotes"]
----
@@ -272,6 +272,13 @@ is being accessed by other components (for example, calling `createEntityManager
calls block until the background bootstrapping has completed. In particular, when you use
Spring Data JPA, make sure to set up deferred bootstrapping for its repositories as well.
As of 6.2, JPA initialization is enforced before context refresh completion, waiting for
asynchronous bootstrapping to complete by then. This makes the availability of the fully
initialized database infrastructure predictable and allows for custom post-initialization
logic in `ContextRefreshedEvent` listeners etc. Putting such application-level database
initialization into `@PostConstruct` methods or the like is not recommended; this is
better placed in `Lifecycle.start` (if applicable) or a `ContextRefreshedEvent` listener.
[[orm-jpa-dao]]
== Implementing DAOs Based on JPA: `EntityManagerFactory` and `EntityManager`
@@ -442,12 +442,32 @@ xref:data-access/transaction/declarative/rolling-back.adoc#transaction-declarati
for further details on rollback rule semantics, patterns, and warnings
regarding possible unintentional matches for pattern-based rollback rules.
[NOTE]
====
As of 6.2, you can globally change the default rollback behavior for example, through
`@EnableTransactionManagement(rollbackOn=ALL_EXCEPTIONS)`, leading to a rollback
for all exceptions raised within a transaction, including any checked exception.
For further customizations, `AnnotationTransactionAttributeSource` provides an
`addDefaultRollbackRule(RollbackRuleAttribute)` method for custom default rules.
Note that transaction-specific rollback rules override the default behavior but
retain the chosen default for unspecified exceptions. This is the case for
Spring's `@Transactional` as well as JTA's `jakarta.transaction.Transactional`
annotation.
Unless you rely on EJB-style business exceptions with commit behavior, it is
advisable to switch to `ALL_EXCEPTIONS` for consistent rollback semantics even
in case of a (potentially accidental) checked exception. Also, it is advisable
to make that switch for Kotlin-based applications where there is no enforcement
of checked exceptions at all.
====
Currently, you cannot have explicit control over the name of a transaction, where 'name'
means the transaction name that appears in a transaction monitor and in logging output.
For declarative transactions, the transaction name is always the fully-qualified class
name + `.` + the method name of the transactionally advised class. For example, if the
name of the transactionally advised class + `.` + the method name. For example, if the
`handlePayment(..)` method of the `BusinessService` class started a transaction, the
name of the transaction would be: `com.example.BusinessService.handlePayment`.
name of the transaction would be `com.example.BusinessService.handlePayment`.
[[tx-multiple-tx-mgrs-with-attransactional]]
== Multiple Transaction Managers with `@Transactional`
@@ -531,12 +551,32 @@ transaction managers, differentiated by the `order`, `account`, and `reactive-ac
qualifiers. The default `<tx:annotation-driven>` target bean name, `transactionManager`,
is still used if no specifically qualified `TransactionManager` bean is found.
[TIP]
====
If all transactional methods on the same class share the same qualifier, consider
declaring a type-level `org.springframework.beans.factory.annotation.Qualifier`
annotation instead. If its value matches the qualifier value (or bean name) of a
specific transaction manager, that transaction manager is going to be used for
transaction definitions without a specific qualifier on `@Transactional` itself.
Such a type-level qualifier can be declared on the concrete class, applying to
transaction definitions from a base class as well. This effectively overrides
the default transaction manager choice for any unqualified base class methods.
Last but not least, such a type-level bean qualifier can serve multiple purposes,
e.g. with a value of "order" it can be used for autowiring purposes (identifying
the order repository) as well as transaction manager selection, as long as the
target beans for autowiring as well as the associated transaction manager
definitions declare the same qualifier value. Such a qualifier value only needs
to be unique within a set of type-matching beans, not having to serve as an ID.
====
[[tx-custom-attributes]]
== Custom Composed Annotations
If you find you repeatedly use the same attributes with `@Transactional` on many different
methods, xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[Spring's meta-annotation support] lets you
define custom composed annotations for your specific use cases. For example, consider the
If you find you repeatedly use the same attributes with `@Transactional` on many different methods,
xref:core/beans/classpath-scanning.adoc#beans-meta-annotations[Spring's meta-annotation support]
lets you define custom composed annotations for your specific use cases. For example, consider the
following annotation definitions:
[tabs]
@@ -23,7 +23,7 @@ As of Spring Framework 5.2, the default configuration also provides support for
Vavr's `Try` method to trigger transaction rollbacks when it returns a 'Failure'.
This allows you to handle functional-style errors using Try and have the transaction
automatically rolled back in case of a failure. For more information on Vavr's Try,
refer to the {vavr-docs}/#_try[official Vavr documentation].
refer to the https://docs.vavr.io/#_try[official Vavr documentation].
Here's an example of how to use Vavr's Try with a transactional method:
[tabs]
+3 -1
View File
@@ -29,6 +29,8 @@ Brannen, Ramnivas Laddad, Arjen Poutsma, Chris Beams, Tareq Abedrabbo, Andy Clem
Syer, Oliver Gierke, Rossen Stoyanchev, Phillip Webb, Rob Winch, Brian Clozel, Stephane
Nicoll, Sebastien Deleuze, Jay Bryant, Mark Paluch
Copyright © 2002 - 2024 VMware, Inc. All Rights Reserved.
Copies of this document may be made for your own use and for distribution to others,
provided that you do not charge any fee for such copies and further provided that each
copy contains the Copyright Notice, whether distributed in print or electronically.
copy contains this Copyright Notice, whether distributed in print or electronically.
@@ -332,7 +332,7 @@ metadata, such as the argument names. The following table describes the items ma
available to the context so that you can use them for key and conditional computations:
[[cache-spel-context-tbl]]
.Cache metadata available in SpEL expressions
.Cache SpEL available metadata
|===
| Name| Location| Description| Example
@@ -358,7 +358,7 @@ available to the context so that you can use them for key and conditional comput
| `args`
| Root object
| The arguments (as an object array) used for invoking the target
| The arguments (as array) used for invoking the target
| `#root.args[0]`
| `caches`
@@ -368,10 +368,9 @@ available to the context so that you can use them for key and conditional comput
| Argument name
| Evaluation context
| The name of a particular method argument. If the names are not available
(for example, because the code was compiled without the `-parameters` flag), individual
arguments are also available using the `#a<#arg>` syntax where `<#arg>` stands for the
argument index (starting from 0).
| Name of any of the method arguments. If the names are not available
(perhaps due to having no debug information), the argument names are also available under the `#a<#arg>`
where `#arg` stands for the argument index (starting from `0`).
| `#iban` or `#a0` (you can also use `#p0` or `#p<#arg>` notation as an alias).
| `result`
@@ -519,41 +518,9 @@ disable it by removing only one configuration line rather than all the annotatio
your code).
To enable caching annotations add the annotation `@EnableCaching` to one of your
`@Configuration` classes:
`@Configuration` classes or use the `cache:annotation-driven` element with XML:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableCaching
class AppConfig {
@Bean
CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCacheSpecification(...);
return cacheManager;
}
}
----
Alternatively, for XML configuration you can use the `cache:annotation-driven` element:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven/>
<bean id="cacheManager" class="org.springframework.cache.caffeine.CaffeineCacheManager">
<property name="cacheSpecification" value="..."/>
</bean>
</beans>
----
include-code::./CacheConfiguration[tag=snippet,indent=0]
Both the `cache:annotation-driven` element and the `@EnableCaching` annotation let you
specify various options that influence the way the caching behavior is added to the
@@ -13,18 +13,7 @@ The JDK-based `Cache` implementation resides under
`org.springframework.cache.concurrent` package. It lets you use `ConcurrentHashMap`
as a backing `Cache` store. The following example shows how to configure two caches:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- simple cache manager -->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="books"/>
</set>
</property>
</bean>
----
include-code::./CacheConfiguration[tag=snippet,indent=0]
The preceding snippet uses the `SimpleCacheManager` to create a `CacheManager` for the
two nested `ConcurrentMapCache` instances named `default` and `books`. Note that the
@@ -52,26 +41,12 @@ of Caffeine.
The following example configures a `CacheManager` that creates the cache on demand:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="cacheManager"
class="org.springframework.cache.caffeine.CaffeineCacheManager"/>
----
include-code::./CacheConfiguration[tag=snippet,indent=0]
You can also provide the caches to use explicitly. In that case, only those
are made available by the manager. The following example shows how to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="cacheManager" class="org.springframework.cache.caffeine.CaffeineCacheManager">
<property name="cacheNames">
<set>
<value>default</value>
<value>books</value>
</set>
</property>
</bean>
----
include-code::./CustomCacheConfiguration[tag=snippet,indent=0]
The Caffeine `CacheManager` also supports custom `Caffeine` and `CacheLoader`.
See the https://github.com/ben-manes/caffeine/wiki[Caffeine documentation]
@@ -97,15 +72,7 @@ implementation is located in the `org.springframework.cache.jcache` package.
Again, to use it, you need to declare the appropriate `CacheManager`.
The following example shows how to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="cacheManager"
class="org.springframework.cache.jcache.JCacheCacheManager"
p:cache-manager-ref="jCacheManager"/>
<!-- JSR-107 cache manager setup -->
<bean id="jCacheManager" .../>
----
include-code::./CacheConfiguration[tag=snippet,indent=0]
[[cache-store-configuration-noop]]
@@ -119,18 +86,7 @@ cache declarations (which can prove tedious), you can wire in a simple dummy cac
performs no caching -- that is, it forces the cached methods to be invoked every time.
The following example shows how to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
<property name="cacheManagers">
<list>
<ref bean="jdkCache"/>
<ref bean="gemfireCache"/>
</list>
</property>
<property name="fallbackToNoOpCache" value="true"/>
</bean>
----
include-code::./CacheConfiguration[tag=snippet,indent=0]
The `CompositeCacheManager` in the preceding chains multiple `CacheManager` instances and,
through the `fallbackToNoOpCache` flag, adds a no-op cache for all the definitions not
@@ -24,7 +24,7 @@ To create the archive, two additional JVM flags must be specified:
* `-Dspring.context.exit=onRefresh`: starts and then immediately exits your Spring
application as described above
To create a CDS archive, your JDK/JRE must have a base image. If you add the flags above to
To create a CDS archive, your JDK must have a base image. If you add the flags above to
your startup script, you may get a warning that looks like this:
[source,shell,indent=0,subs="verbatim"]
@@ -32,8 +32,7 @@ your startup script, you may get a warning that looks like this:
-XX:ArchiveClassesAtExit is unsupported when base CDS archive is not loaded. Run with -Xlog:cds for more info.
----
The base CDS archive is usually provided out-of-the-box, but can also be created if needed by issuing the following
command:
The base CDS archive can be created by issuing the following command:
[source,shell,indent=0,subs="verbatim"]
----
@@ -45,9 +44,6 @@ command:
Once the archive is available, add `-XX:SharedArchiveFile=application.jsa` to your startup
script to use it, assuming an `application.jsa` file in the working directory.
To check if the CDS cache is effective, you can use (for testing purposes only, not in production) `-Xshare:on` which
prints an error message and exits if CDS can't be enabled.
To figure out how effective the cache is, you can enable class loading logs by adding
an extra attribute: `-Xlog:class+load:file=cds.log`. This creates a `cds.log` with every
attempt to load a class and its source. Classes that are loaded from the cache should have
@@ -62,11 +58,8 @@ a "shared objects file" source, as shown in the following example:
[0.065s][info][class,load] org.springframework.context.MessageSource source: shared objects file (top)
----
If CDS can't be enabled or if you have a large number of classes that are not loaded from the cache, make sure that
the following conditions are fulfilled when creating and using the archive:
- The very same JVM must be used.
- The classpath must be specified as a list of JARs, and avoid the usage of directories and `*` wildcard characters.
- The timestamps of the JARs must be preserved.
- When using the archive, the classpath must be the same than the one used to create the archive, in the same order.
Additional JARs or directories can be specified *at the end* (but won't be cached).
TIP: If you have a large number of classes that are not loaded from the cache, make sure that
the JDK and classpath used by the commands that create the archive and start the application
are identical. Note also that to effectively cache classes, the classpath should be specified
as a list of JARs containing those classes, and avoid the usage of directories and `*`
wildcard characters.
@@ -19,8 +19,6 @@ A checkpoint can be created on demand, for example using a command like `jcmd ap
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.
WARNING: Be aware that when defining scheduling tasks at a fixed rate, for example with an annotation like `@Scheduled(fixedRate = 5000)`, all missed executions between checkpoint and restore will be performed when the JVM is restored with on-demand checkpoint/restore. If this is not the behavior you want, it is recommended to schedule tasks at a fixed delay (for example with `@Scheduled(fixedDelay = 5000)`) or with a cron expression as those are calculated after every task execution.
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
@@ -41,14 +41,7 @@ JavaMail features, such as MIME message support to the `MailSender` interface
Assume that we have a business interface called `OrderManager`, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface OrderManager {
void placeOrder(Order order);
}
----
include-code::./OrderManager[tag=snippet,indent=0]
Further assume that we have a requirement stating that an email message with an
order number needs to be generated and sent to a customer who placed the relevant order.
@@ -60,70 +53,11 @@ order number needs to be generated and sent to a customer who placed the relevan
The following example shows how to use `MailSender` and `SimpleMailMessage` to send an
email when someone places an order:
[source,java,indent=0,subs="verbatim,quotes"]
----
import org.springframework.mail.MailException;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
public class SimpleOrderManager implements OrderManager {
private MailSender mailSender;
private SimpleMailMessage templateMessage;
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
public void setTemplateMessage(SimpleMailMessage templateMessage) {
this.templateMessage = templateMessage;
}
public void placeOrder(Order order) {
// Do the business calculations...
// Call the collaborators to persist the order...
// Create a thread-safe "copy" of the template message and customize it
SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage);
msg.setTo(order.getCustomer().getEmailAddress());
msg.setText(
"Dear " + order.getCustomer().getFirstName()
+ order.getCustomer().getLastName()
+ ", thank you for placing order. Your order number is "
+ order.getOrderNumber());
try {
this.mailSender.send(msg);
}
catch (MailException ex) {
// simply log it and go on...
System.err.println(ex.getMessage());
}
}
}
----
include-code::./SimpleOrderManager[tag=snippet,indent=0]
The following example shows the bean definitions for the preceding code:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="mail.mycompany.example"/>
</bean>
<!-- this is a template message that we can pre-load with default state -->
<bean id="templateMessage" class="org.springframework.mail.SimpleMailMessage">
<property name="from" value="customerservice@mycompany.example"/>
<property name="subject" value="Your order"/>
</bean>
<bean id="orderManager" class="com.mycompany.businessapp.support.SimpleOrderManager">
<property name="mailSender" ref="mailSender"/>
<property name="templateMessage" ref="templateMessage"/>
</bean>
----
include-code::./MailConfiguration[tag=snippet,indent=0]
[[mail-usage-mime]]
@@ -37,28 +37,12 @@ declarations to it.
To enable support for `@JmsListener` annotations, you can add `@EnableJms` to one of
your `@Configuration` classes, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableJms
public class AppConfig {
@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setDestinationResolver(destinationResolver());
factory.setSessionTransacted(true);
factory.setConcurrency("3-10");
return factory;
}
}
----
include-code::./JmsConfiguration[tag=snippet,indent=0]
By default, the infrastructure looks for a bean named `jmsListenerContainerFactory`
as the source for the factory to use to create message listener containers. In this
case (and ignoring the JMS infrastructure setup), you can invoke the `processOrder`
method with a core pool size of three threads and a maximum pool size of ten threads.
method with a core poll size of three threads and a maximum pool size of ten threads.
You can customize the listener container factory to use for each annotation or you can
configure an explicit default by implementing the `JmsListenerConfigurer` interface.
@@ -67,22 +51,6 @@ container factory. See the javadoc of classes that implement
{spring-framework-api}/jms/annotation/JmsListenerConfigurer.html[`JmsListenerConfigurer`]
for details and examples.
If you prefer xref:integration/jms/namespace.adoc[XML configuration], you can use the `<jms:annotation-driven>`
element, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<jms:annotation-driven/>
<bean id="jmsListenerContainerFactory"
class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destinationResolver" ref="destinationResolver"/>
<property name="sessionTransacted" value="true"/>
<property name="concurrency" value="3-10"/>
</bean>
----
[[jms-annotated-programmatic-registration]]
== Programmatic Endpoint Registration
@@ -7,62 +7,13 @@ automatically determine the `ActivationSpec` class name from the provider's
`ResourceAdapter` class name. Therefore, it is typically possible to provide
Spring's generic `JmsActivationSpecConfig`, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean class="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager">
<property name="resourceAdapter" ref="resourceAdapter"/>
<property name="activationSpecConfig">
<bean class="org.springframework.jms.listener.endpoint.JmsActivationSpecConfig">
<property name="destinationName" value="myQueue"/>
</bean>
</property>
<property name="messageListener" ref="myMessageListener"/>
</bean>
----
include-code::./JmsConfiguration[tag=snippet,indent=0]
Alternatively, you can set up a `JmsMessageEndpointManager` with a given
`ActivationSpec` object. The `ActivationSpec` object may also come from a JNDI lookup
(using `<jee:jndi-lookup>`). The following example shows how to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean class="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager">
<property name="resourceAdapter" ref="resourceAdapter"/>
<property name="activationSpec">
<bean class="org.apache.activemq.ra.ActiveMQActivationSpec">
<property name="destination" value="myQueue"/>
<property name="destinationType" value="jakarta.jms.Queue"/>
</bean>
</property>
<property name="messageListener" ref="myMessageListener"/>
</bean>
----
Using Spring's `ResourceAdapterFactoryBean`, you can configure the target `ResourceAdapter`
locally, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="resourceAdapter" class="org.springframework.jca.support.ResourceAdapterFactoryBean">
<property name="resourceAdapter">
<bean class="org.apache.activemq.ra.ActiveMQResourceAdapter">
<property name="serverUrl" value="tcp://localhost:61616"/>
</bean>
</property>
<property name="workManager">
<bean class="org.springframework.jca.work.SimpleTaskWorkManager"/>
</property>
</bean>
----
The specified `WorkManager` can also point to an environment-specific thread pool --
typically through a `SimpleTaskWorkManager` instance's `asyncTaskExecutor` property.
Consider defining a shared thread pool for all your `ResourceAdapter` instances
if you happen to use multiple adapters.
In some environments, you can instead obtain the entire `ResourceAdapter` object from JNDI
(by using `<jee:jndi-lookup>`). The Spring-based message listeners can then interact with
the server-hosted `ResourceAdapter`, which also use the server's built-in `WorkManager`.
include-code::./AlternativeJmsConfiguration[tag=snippet,indent=0]
See the javadoc for {spring-framework-api}/jms/listener/endpoint/JmsMessageEndpointManager.html[`JmsMessageEndpointManager`],
{spring-framework-api}/jms/listener/endpoint/JmsActivationSpecConfig.html[`JmsActivationSpecConfig`],
@@ -31,30 +31,7 @@ on multiple threads, it is important to ensure that your implementation is threa
The following example shows a simple implementation of an MDP:
[source,java,indent=0,subs="verbatim,quotes"]
----
import jakarta.jms.JMSException;
import jakarta.jms.Message;
import jakarta.jms.MessageListener;
import jakarta.jms.TextMessage;
public class ExampleListener implements MessageListener {
public void onMessage(Message message) {
if (message instanceof TextMessage textMessage) {
try {
System.out.println(textMessage.getText());
}
catch (JMSException ex) {
throw new RuntimeException(ex);
}
}
else {
throw new IllegalArgumentException("Message must be of type TextMessage");
}
}
}
----
include-code::./ExampleListener[tag=snippet,indent=0]
Once you have implemented your `MessageListener`, it is time to create a message listener
container.
@@ -62,18 +39,7 @@ container.
The following example shows how to define and configure one of the message listener
containers that ships with Spring (in this case, `DefaultMessageListenerContainer`):
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="jmsexample.ExampleListener"/>
<!-- and this is the message listener container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
</bean>
----
include-code::./JmsConfiguration[tag=snippet,indent=0]
See the Spring javadoc of the various message listener containers (all of which implement
{spring-framework-api}/jms/listener/MessageListenerContainer.html[MessageListenerContainer])
@@ -123,19 +89,7 @@ messaging support. In a nutshell, it lets you expose almost any class as an MDP
Consider the following interface definition:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface MessageDelegate {
void handleMessage(String message);
void handleMessage(Map message);
void handleMessage(byte[] message);
void handleMessage(Serializable message);
}
----
include-code::./MessageDelegate[tag=snippet,indent=0]
Notice that, although the interface extends neither the `MessageListener` nor the
`SessionAwareMessageListener` interface, you can still use it as an MDP by using the
@@ -145,33 +99,13 @@ receive and handle.
Now consider the following implementation of the `MessageDelegate` interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
public class DefaultMessageDelegate implements MessageDelegate {
// implementation elided for clarity...
}
----
include-code::./DefaultMessageDelegate[tag=snippet,indent=0]
In particular, note how the preceding implementation of the `MessageDelegate` interface (the
`DefaultMessageDelegate` class) has no JMS dependencies at all. It truly is a
POJO that we can make into an MDP through the following configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- this is the Message Driven POJO (MDP) -->
<bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="jmsexample.DefaultMessageDelegate"/>
</constructor-arg>
</bean>
<!-- and this is the message listener container... -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
</bean>
----
include-code::./JmsConfiguration[tag=snippet,indent=0]
The next example shows another MDP that can handle only receiving JMS
`TextMessage` messages. Notice how the message handling method is actually called
@@ -181,38 +115,15 @@ also how the `receive(..)` method is strongly typed to receive and respond only
`TextMessage` messages.
The following listing shows the definition of the `TextMessageDelegate` interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface TextMessageDelegate {
void receive(TextMessage message);
}
----
include-code::./TextMessageDelegate[tag=snippet,indent=0]
The following listing shows a class that implements the `TextMessageDelegate` interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
public class DefaultTextMessageDelegate implements TextMessageDelegate {
// implementation elided for clarity...
}
----
include-code::./DefaultTextMessageDelegate[tag=snippet,indent=0]
The configuration of the attendant `MessageListenerAdapter` would then be as follows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="messageListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="jmsexample.DefaultTextMessageDelegate"/>
</constructor-arg>
<property name="defaultListenerMethod" value="receive"/>
<!-- we don't want automatic message context extraction -->
<property name="messageConverter">
<null/>
</property>
</bean>
----
include-code::./MessageListenerConfiguration[tag=snippet,indent=0]
Note that, if the `messageListener` receives a JMS `Message` of a type
other than `TextMessage`, an `IllegalStateException` is thrown (and subsequently
@@ -220,21 +131,9 @@ swallowed). Another of the capabilities of the `MessageListenerAdapter` class is
ability to automatically send back a response `Message` if a handler method returns a
non-void value. Consider the following interface and class:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface ResponsiveTextMessageDelegate {
include-code::./ResponsiveTextMessageDelegate[tag=snippet,indent=0]
// notice the return type...
String receive(TextMessage message);
}
----
[source,java,indent=0,subs="verbatim,quotes"]
----
public class DefaultResponsiveTextMessageDelegate implements ResponsiveTextMessageDelegate {
// implementation elided for clarity...
}
----
include-code::./DefaultResponsiveTextMessageDelegate[tag=snippet,indent=0]
If you use the `DefaultResponsiveTextMessageDelegate` in conjunction with a
`MessageListenerAdapter`, any non-null value that is returned from the execution of
@@ -264,15 +163,7 @@ has committed but message processing failed to commit.
Consider the following bean definition:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
<property name="sessionTransacted" value="true"/>
</bean>
----
include-code::./JmsConfiguration[tag=snippet,indent=0]
To participate in an externally managed transaction, you need to configure a
transaction manager and use a listener container that supports externally managed
@@ -288,24 +179,9 @@ semantics, at the expense of XA transaction log overhead).
The following bean definition creates a transaction manager:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
----
include-code::./ExternalTxJmsConfiguration[tag=transactionManagerSnippet,indent=0]
Then we need to add it to our earlier container configuration. The container
takes care of the rest. The following example shows how to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="destination"/>
<property name="messageListener" ref="messageListener"/>
<property name="transactionManager" ref="transactionManager"/> <1>
</bean>
----
<1> Our transaction manager.
include-code::./ExternalTxJmsConfiguration[tag=jmsContainerSnippet,indent=0]

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