Compare commits

..

108 Commits

Author SHA1 Message Date
Brian Clozel 99a366baf6 Next development version (v6.1.22-SNAPSHOT) 2025-06-12 10:56:22 +02:00
Brian Clozel 498ccda8fc Upgrade to Gradle 8.14.2 2025-06-12 10:00:55 +02:00
Brian Clozel fd68ea6fcb Encode non-printable character in Content-Disposition parameter
Prior to this commit, the "filename" parameter value for the
"Content-Disposition" header would contain non-printable characters,
causing parsing issues for HTTP clients.
This commit ensures that all non-printable characters are encoded.

Fixes gh-35035
2025-06-12 08:39:41 +02:00
Juergen Hoeller 28caa39020 Upgrade to Reactor 2023.0.19
Includes SLF4J 2.0.17, Groovy 4.0.27, FreeMarker 2.3.34

Closes gh-35022
2025-06-11 12:54:02 +02:00
Sam Brannen 8ecc553696 Polish contribution
See gh-35013
See gh-35018

(cherry picked from commit 4d2cc4ae97)
2025-06-10 12:39:48 +02:00
Mohammad Saeed Nouri cd44efaf68 Allow update of existing WebSession after max sessions limit is reached
Previously, when saving a WebSession, the system did not check whether
the session ID already existed. As a result, even if the session being
saved was an update to an existing one, it was incorrectly treated as a
new session, and a "maximum sessions exceeded" error was triggered.

This fix ensures that if a WebSession with the same ID already exists,
it will be updated rather than counted as a new session, thereby
preventing unnecessary session limit violations.

See gh-35013
Closes gh-35018

Signed-off-by: Mohammad Saeed Nouri <msnsaeed71@gmail.com>
(cherry picked from commit c04902fefb)
2025-06-10 12:38:58 +02:00
Sam Brannen 59d2895c82 Fix InMemoryWebSessionStoreTests.startsSessionImplicitly() test
(cherry picked from commit 3c265e1044)
2025-06-10 12:37:45 +02:00
Sam Brannen a876bb41af Polish WebSession support and tests
(cherry picked from commit 222702f750)
2025-06-10 12:37:19 +02:00
Juergen Hoeller 3b6becac01 Check for package-visible constructor in case of ClassLoader mismatch
Closes gh-34950

(cherry picked from commit 15d1455acb)
2025-05-27 10:30:07 +02:00
Sam Brannen 59ffbd7a59 Test conversion support in PropertySourcesPlaceholderConfigurer
This commit introduces a "regression test" which demonstrates that
PropertySourcesPlaceholderConfigurer uses the ConversionService from
the Environment.

See gh-34936
2025-05-26 18:09:51 +02:00
Sam Brannen c0a9da65f4 Support only Object property values in MockEnvironment test fixture
The setProperty() and withProperty() methods in MockEnvironment were
originally introduced with (String, String) signatures; however, they
should have always had (String, Object) signatures in order to comply
with the MockPropertySource and PropertySource APIs.

To address that, this commit changes the signatures of these methods so
that they only accept Object values for properties.

NOTE: this commit only affects the internal MockEnvironment used as a
test fixture. This commit does not affect the official, public
MockEnvironment implementation in spring-test.

See gh-34947
See gh-34948

(cherry picked from commit d78264756e)
2025-05-26 17:58:15 +02:00
Sam Brannen 30bee4db0f Polish Javadoc for MockPropertySource
(cherry picked from commit 6a6abac003)
2025-05-26 17:54:52 +02:00
Brian Clozel 9a89654a64 Next development version (v6.1.21-SNAPSHOT) 2025-05-15 10:32:10 +02:00
rstoyanchev edfcc6ffb1 Make use of PatternMatchUtils ignoreCase option
Closes gh-34801
2025-05-14 15:49:06 +01:00
rstoyanchev f93132b11e Add missing @since tags in PatternMatchUtils
See: gh-34801
2025-05-14 15:17:56 +01:00
Juergen Hoeller 6ab4c84bd5 Upgrade to Reactor 2023.0.18
Closes gh-34899
2025-05-14 15:25:15 +02:00
Juergen Hoeller d5fca0d2c5 Upgrade to Jetty 12.0.21, Netty 4.1.121, Apache HttpClient 5.4.4, Checkstyle 10.23.1 2025-05-13 16:57:19 +02:00
Juergen Hoeller cbb94193fe Clarify CompositePropertySource behavior for EnumerablePropertySource contract
Closes gh-34886

(cherry picked from commit 6a9444473f)
2025-05-13 16:57:06 +02:00
Brian Clozel 5b5e2b6876 Fix HttpClient 5.3.x request config compatibility
As of gh-33806, the HttpComponents client request factory is forward
compatible with the 5.4+ versions of that library for configuring HTTP
request configuration.

This change would not tkae into account configuration set at the Spring
level because it would consider the default `RequestConfig` instance as
a custom one. This commit ensures that Spring sets its own configuration
if no custom configuration was set for all supported HttpComponents
generations.

Fixes gh-34854
2025-05-05 14:50:46 +02:00
Juergen Hoeller a5b0399a1d Polishing
(cherry picked from commit d0b186a1c7)
2025-04-28 16:20:29 +02:00
Juergen Hoeller 71f2725638 Try loadClass on LinkageError in case of same ClassLoader as well
Closes gh-34824

(cherry picked from commit 4172581f1b)
2025-04-28 16:20:24 +02:00
Sam Brannen daee9f1242 Reinstate the @⁠Inject Technology Compatibility Kit (TCK)
In commit 05ebca8677, the `public` modifier was removed from the
SpringAtInjectTckTests class, which prevents it from being run as a
JUnit 3 test class.

To address that, this commit adds the missing `public` modifier as well
as a a code comment to help prevent this from happening again.

In addition, this commit updates spring-context.gradle to ensure that
the JUnit Vintage test engine is always applied. However, that Gradle
configuration is unfortunately ignored due to how our TestConventions
class has been implemented. Thus, that issue will have to be addressed
separately.

Closes gh-34800

(cherry picked from commit e384389790)
2025-04-25 12:37:58 +02:00
rstoyanchev 89ae20b9ff Add ignoreCase variants to PatternMatchUtils
See gh-34801
2025-04-23 12:13:49 +01:00
Brian Clozel a5a7d63dab Next development version (v6.1.20-SNAPSHOT) 2025-04-17 09:05:28 +02:00
Stéphane Nicoll d879112885 Upgrade to github-changelog-generator 0.0.12
Closes gh-34756
2025-04-15 10:06:17 +02:00
Juergen Hoeller 0a71df7aee Polishing 2025-04-10 18:55:34 +02:00
Juergen Hoeller 82b7fcb2cc Try loadClass on LinkageError in case of ClassLoader mismatch
See gh-34677

(cherry picked from commit 7f2c1f447f)
2025-04-10 18:49:31 +02:00
Juergen Hoeller 6717fca4ec Explicitly use original ClassLoader in case of package visibility
Closes gh-34684

(cherry picked from commit 6bb964e2d0)
2025-04-02 23:50:39 +02:00
Juergen Hoeller 86ea7003f7 Upgrade to Groovy 4.0.26, Jetty 12.0.18, Apache HttpClient 5.4.3, Checkstyle 10.22 2025-04-01 23:42:56 +02:00
Juergen Hoeller 0a157faec1 Polishing (backported from 6.2.x) 2025-04-01 22:36:40 +02:00
Juergen Hoeller 2548d44f3b Include cause in MethodInvocationException message
Closes gh-34691

(cherry picked from commit 203ca30a64)
2025-04-01 22:28:39 +02:00
Juergen Hoeller 6cc6ea1b2b Make jar caching configurable through setUseCaches
Closes gh-34694
2025-03-31 17:24:33 +02:00
Juergen Hoeller b73ca60811 Only attempt load for CGLIB classes in AOT mode
Closes gh-34677

(cherry picked from commit 743f32675d)
2025-03-31 17:23:46 +02:00
Sam Brannen d689629036 Support abstract @⁠Configuration classes without @⁠Bean methods again
Historically, @⁠Configuration classes that did not declare @⁠Bean
methods were allowed to be abstract. However, the changes made in
76a6b9ea79 introduced a regression that prevents such classes from
being abstract, resulting in a BeanInstantiationException. This change
in behavior is caused by the fact that such a @⁠Configuration class is
no longer replaced by a concrete subclass created dynamically by CGLIB.

This commit restores support for abstract @⁠Configuration classes
without @⁠Bean methods by modifying the "no enhancement required" check
in ConfigurationClassParser.

See gh-34486
Closes gh-34663

(cherry picked from commit 044258f085)
2025-03-31 14:33:02 +02:00
rstoyanchev 077721bdb4 Remove outdated notes on forwarded headers.
Closes gh-34625
2025-03-31 11:38:51 +01:00
Stéphane Nicoll b7654dd984 Make sure the generated values are available from a static context
This commit updates the tests of property values code generated to
invoke the generated code from a `static` context. This ensures that
the test fails if that's not the case.

This commit also updated LinkedHashMap handling that did suffer from
that problem.

Closes gh-34661
2025-03-27 12:09:29 +01:00
Juergen Hoeller 4645ce60c8 Polishing 2025-03-25 19:27:37 +01:00
Juergen Hoeller 564d3f6f5e Enable qualifier annotation tests including backported precedence tests
See gh-34644
2025-03-25 00:24:23 +01:00
Juergen Hoeller 7a1dfe79a0 Polishing (aligned with 6.2.x) 2025-03-21 16:08:02 +01:00
Juergen Hoeller 19257f4fdb Add javadoc notes on potential exception suppression in getBeansOfType
Closes gh-34629

(cherry picked from commit dc41ff569e)
2025-03-21 16:02:01 +01:00
Juergen Hoeller f06167ea01 Polishing 2025-03-21 11:06:10 +01:00
rstoyanchev 618e003b91 Fix dated Javadoc in MvcUriComponentsBuilder
related to forwarded headers

Closes gh-34615
2025-03-19 12:37:12 +00:00
Juergen Hoeller 8c494e5165 Defer triggerAfterCompletion invocation in doRollbackOnCommitException
Closes gh-34595

(cherry picked from commit cc986cd2e8)
2025-03-19 11:04:51 +01:00
Juergen Hoeller 62d7396769 Suggest compilation with -parameters in case of ambiguity
Closes gh-34609

(cherry picked from commit 86b2617c7f)
2025-03-19 11:04:39 +01:00
Brian Clozel 252058a65a Next development version (v6.1.19-SNAPSHOT) 2025-03-13 09:00:08 +01:00
Juergen Hoeller 9ea049ad6f Upgrade to Reactor 2023.0.16
Includes Netty 4.1.119 and Jetty 12.0.17

Closes gh-34579
2025-03-12 13:28:03 +01:00
Juergen Hoeller 1e31cd685e Avoid getTargetConnection call on transaction-aware Connection close
Closes gh-34484

(cherry picked from commit c64dae3623)
2025-02-28 14:21:55 +01:00
Juergen Hoeller 0d60f266ad Defensively call isShutdown method for executor description
Closes gh-34514

(cherry picked from commit 559ea6c480)
2025-02-28 14:21:41 +01:00
Juergen Hoeller a018ae6e02 Lenient fallback when cached WeakReference returns null
Closes gh-34423
2025-02-26 10:25:07 +01:00
Juergen Hoeller ea419d2dcd Avoid unnecessary CGLIB processing on configuration classes
Closes gh-34486

(cherry picked from commit aff9ac72ec)
2025-02-25 18:42:18 +01:00
Brian Clozel c02d07fdef Fix multiple Content-Language values in MockHttpServletResponse
Prior to this commit, `MockHttpServletResponse` would only support
adding a `Content-Language` once. Adding multiple header values would
always replace the content-language property in the response and the
entire header value.

This commit ensures that this behavior is supported.

Fixes gh-34491
2025-02-25 17:57:27 +01:00
Juergen Hoeller 7460be617b Consistent default ClassLoader fallback in hint classes
Closes gh-34470
2025-02-23 15:23:51 +01:00
Brian Clozel ccf4b028d2 Fix null value support in ContentCachingResponseWrapper
Prior to this commit, calling `setHeader` on the response wrapper would
have a separate code path for the "Content-Length" header. This did not
support calls with `null` values and would result in an exception.

This commit ensures that the cached content length value is reset in
this case and that the call is forwarded properly to the superclass.

Fixes gh-34465
2025-02-21 14:36:09 +01:00
Brian Clozel b5c89c91a9 Handle null values in MockHttpServletResponse#setHeader
Prior to this commit, `MockHttpServletResponse#setHeader` would not
remove the header entry when given a `null` value, as documented in the
Servlet API.
This commit ensures that this behavior is enforced.

Fixes gh-34466
2025-02-21 14:35:37 +01:00
Sébastien Deleuze 01ae0a7c26 Fix broken antora task
See https://github.com/spring-io/antora-extensions/pull/43

Closes gh-34455
2025-02-19 16:45:08 +01:00
Juergen Hoeller b48d72cbd0 Apply fallback in case of initial SmartClassLoader mismatch as well
See gh-34423

(cherry picked from commit 6786e1c3e5)
2025-02-18 20:53:21 +01:00
Juergen Hoeller 48628194dc Apply fallback in case of any exception coming out of createClass
Closes gh-34423

(cherry picked from commit 93134fd4d1)
2025-02-18 15:41:38 +01:00
Juergen Hoeller 3cf1fbf222 Mark XML-configured executor/scheduler as infrastructure bean
Closes gh-34015

(cherry picked from commit d0ceefedc6)
2025-02-18 15:41:17 +01:00
Stéphane Nicoll ecd943c7ec Next development version (v6.1.18-SNAPSHOT) 2025-02-13 13:23:32 +01:00
Juergen Hoeller 9d6d67188a Upgrade to Jetty 12.0.16, Netty 4.1.118, XStream 1.4.21, AssertJ 3.27.3, Checkstyle 10.21.2 2025-02-11 22:47:24 +01:00
Juergen Hoeller 24fd0940bd Align with SmartClassLoader handling for AOP proxy classes
Closes gh-34274

(cherry picked from commit f53da04717)
2025-02-11 22:19:43 +01:00
Sam Brannen 5b928f47a8 Finish incomplete sentences
(cherry picked from commit 9d3374b28d)
2025-02-11 12:32:27 +01:00
Stéphane Nicoll 5e52baee86 Upgrade to Reactor 2023.0.15
Closes gh-34406
2025-02-11 11:44:35 +01:00
Stéphane Nicoll c10596a1fd Upgrade to RSocket 1.1.5
Closes gh-34405
2025-02-11 11:43:04 +01:00
Sam Brannen a68b7289f0 Improve warning for unexpected use of value attribute as @⁠Component name
Prior to this commit, if a String 'value' attribute of an annotation
was annotated with @⁠AliasFor and explicitly configured to alias an
attribute other than @⁠Component.value, the value was still used as the
@⁠Component name, but the warning message that was logged stated that
the 'value' attribute should be annotated with
@⁠AliasFor(annotation=Component.class). However, it is not possible to
annotate an annotation attribute twice with @⁠AliasFor.

To address that, this commit revises the logic in
AnnotationBeanNameGenerator so that it issues a log message similar to
the following in such scenarios.

WARN o.s.c.a.AnnotationBeanNameGenerator - Although the 'value'
attribute in @⁠example.MyStereotype declares @⁠AliasFor for an
attribute other than @⁠Component's 'value' attribute, the value is
still used as the @⁠Component name based on convention. As of Spring
Framework 7.0, such a 'value' attribute will no longer be used as the
@⁠Component name.

See gh-34346
Closes gh-34317

(cherry picked from commit 17a94fb110)
2025-02-10 13:32:28 +01:00
Sam Brannen a5b9e667e3 Polishing
(cherry picked from commit 2fcae65853)
2025-02-10 13:32:20 +01:00
Branden Clark 78fe55f4d1 Check hasNext on sessionIds in UserDestinationResult
Closes gh-34333

Signed-off-by: Branden Clark <brandenrayclark@gmail.com>
2025-02-10 11:22:28 +00:00
Brian Clozel 65913c3655 Fix "Nth day of week" Quartz-style cron expressions
Prior to this commit, `CronExpression` would support Quartz-style
expressions with "Nth occurence of a  dayOfWeek" semantics by using the
`TemporalAdjusters.dayOfWeekInMonth` JDK support. This method will
return the Nth occurence starting with the month of the given temporal,
but in some cases will overflow to the next or previous month.
This behavior is not expected for our cron expression support.

This commit ensures that when an overflow happens (meaning, the
resulting date is not in the same month as the input temporal), we
should instead have another attempt at finding a valid month for this
expression.

Fixes gh-34377
2025-02-06 18:34:24 +01:00
Sam Brannen b7a996a64b Clarify component scanning of abstract classes with @⁠Lookup methods
Due to changes in gh-19118, classes that contain @⁠Lookup methods are
no longer required to be concrete classes for use with component
scanning; however, the reference documentation still states that such
classes must not be abstract.

This commit therefore removes the outdated reference documentation and
updates the corresponding Javadoc.

See gh-19118
Closes gh-34367

(cherry picked from commit 819a7c86c1)
2025-02-05 13:44:28 +01:00
Stéphane Nicoll 5b1a7c7f21 Handle arbitrary JoinPoint argument index
Closes gh-34369
2025-02-05 11:55:11 +01:00
Brian Clozel ebd80bdd6c Delete failing Freemarker test
This test was already ignored as of Java 21 because of a Java behavior
change, and now it started failing as of 17.0.14.
This commit removes the test entirely.
2025-02-04 10:03:50 +01:00
Juergen Hoeller a4fc68b8e8 Explicitly set custom ClassLoader on CGLIB Enhancer
Closes gh-34274

(cherry picked from commit 1b18928bf0)
2025-02-03 15:37:04 +01:00
Sam Brannen c333946b0b Restore property binding support for a Map that implements Iterable
The changes in commit c20a2e4763 introduced a regression with regard to
binding to a Map property when the Map also happens to implement
Iterable.

Although that is perhaps not a very common scenario, this commit
reorders the if-blocks in AbstractNestablePropertyAccessor's
getPropertyValue(PropertyTokenHolder) method so that a Map is
considered before an Iterable, thereby allowing an Iterable-Map to be
accessed as a Map.

See gh-907
Closes gh-34332

(cherry picked from commit b9e43d05bd)
2025-01-29 17:46:19 +01:00
rstoyanchev b0a8a3ec5f Enhance DisconnectedClientHelper exception type checks
We now look for the target exception types in cause chain as well,
but return false if we encounter a RestClient or WebClient
exception in the chain.

Closes gh-34264
2025-01-21 12:25:45 +00:00
rstoyanchev bb5be2119c Restore exception phrase in DisconnectedClientHelper
Effectively revert 203fa7, and add implementation comment and tests.

See gh-34264
2025-01-21 12:25:35 +00:00
Brian Clozel cf662368a5 Fix Wrong parentId tracking in JFR application startup
This commit fixes the tracking of the main event parentId for the Java
Flight Recorder implementation variant.

Fixes gh-34254
2025-01-13 20:22:07 +01:00
rstoyanchev e1b06ccfaa Improve logging in ReactiveTypeHandler
See gh-34188
2025-01-06 12:33:38 +00:00
rstoyanchev 9eefdb8041 Synchronize in WebAsyncManager onError/onTimeout
On connection loss, in a race between application thread and onError
callback trying to set the DeferredResult and dispatch, the onError
callback must not exit until dispatch completes. Currently, it may do
so because the DeferredResult has checks to bypasses locking or even
trying to dispatch if result is already set.

Closes gh-34192
2025-01-06 12:33:17 +00:00
rstoyanchev c50cb10964 Minor refactoring in WebAsyncManager
There is no need to set the DeferredResult from WebAsyncManager in an
onError notification because it is already done from the Lifecycle
interceptor in DeferredResult.

See gh-34192
2025-01-06 12:33:02 +00:00
rstoyanchev 245341231f Polishing in WebAsyncManager
See gh-34192
2025-01-06 12:32:40 +00:00
Sébastien Deleuze 875cc828cf Upgrade to Java 17.0.13 2024-12-30 12:14:52 +01:00
Stéphane Nicoll 898d3ec86a Backport tests for exact match resolution
See gh-34124
2024-12-23 12:15:44 +01:00
Tran Ngoc Nhan 8ccaabe778 Fix broken links in the web reference documentation
Backport of gh-34115.

Closes gh-34139
2024-12-23 11:22:17 +01:00
Sam Brannen 9c934b5019 Support varargs-only MethodHandle as SpEL function
Prior to this commit, if a MethodHandle was registered as a custom
function in the Spring Expression Language (SpEL) for a static method
that accepted only a variable argument list (for example,
`static String func(String... args)`), attempting to invoke the
registered function within a SpEL expression resulted in a
ClassCastException because the varargs array was unnecessarily wrapped
in an Object[].

This commit modifies the logic in FunctionReference's internal
executeFunctionViaMethodHandle() method to address that.

Closes gh-34109
2024-12-18 15:34:27 +01:00
Brian Clozel 4b45338ae6 Improve PathMatcher/PatternParser XML configuration
Prior to this commit, the MVC namespace for the XML Spring configuration
model would use the `PathMatcher` bean instance when provided like this:

```
<bean id="pathMatcher" class="org.springframework.util.AntPathMatcher"/>
<mvc:annotation-driven>
  <mvc:path-matching path-matcher="pathMatcher"/>
</mvc:annotation-driven>
<mvc:resources mapping="/resources/**" location="classpath:/static/"/>
```

With this configuration, the handler mapping for annotated controller
would use the given `AntPathMatcher` instance but the handler mapping
for resources would still use the default, which is `PathPatternParser`
since 6.0.

This commit ensures that when a custom `path-matcher` is defined, it's
consistently used for all MVC handler mappings as an alias to the
well-known bean name. This allows to use `AntPathMatcher` consistently
while working on a migration path to `PathPatternParser`

This commit also adds a new XML attribute to the path matching
configuration that makes it possible to use a custom `PathPatternParser`
instance:

```
<bean id="patternParser" class="org.springframework.web.util.pattern.PathPatternParser"/>
<mvc:annotation-driven>
  <mvc:path-matching pattern-parser="patternParser"/>
</mvc:annotation-driven>
```

Closes gh-34102
See gh-34064
2024-12-17 10:30:18 +01:00
Juergen Hoeller 95003e3512 Avoid logger serialization behind shared EntityManager proxy
See gh-34084
2024-12-12 19:52:21 +01:00
Stéphane Nicoll 68368621f3 Prevent execution of Antora jobs on forks
Closes gh-34083
2024-12-12 17:39:59 +01:00
Brian Clozel 890a8f4311 Next development version (v6.1.17-SNAPSHOT) 2024-12-12 10:04:11 +01:00
Juergen Hoeller 0976d1a252 Upgrade to RxJava 3.1.10 and Checkstyle 10.20.2 2024-12-11 17:49:18 +01:00
Juergen Hoeller 2c0ce790d8 Avoid deprecated ListenableFuture name for internal class 2024-12-11 17:47:11 +01:00
Juergen Hoeller d512aafc36 Avoid javadoc references to deprecated types/methods
(cherry picked from commit 68997d8416)
2024-12-11 17:47:03 +01:00
Sam Brannen faa000f330 Log alias removal in DefaultListableBeanFactory
Prior to this commit, information was logged when a bean definition
overrode an existing bean definition, but nothing was logged when the
registration of a bean definition resulted in the removal of an alias.

With this commit, an INFO message is now logged whenever an alias is
removed in DefaultListableBeanFactory.

Closes gh-34070

(cherry picked from commit 41d9f21ab9)
2024-12-11 15:05:42 +01:00
Juergen Hoeller daf1b3d7a7 Remove unnecessary downcast to DefaultListableBeanFactory
See gh-33920
See gh-25952
2024-12-10 22:33:01 +01:00
Juergen Hoeller a48897a241 Log provider setup failure at info level without stacktrace
Closes gh-33979

(cherry picked from commit 3e3ca74020)
2024-12-10 22:28:38 +01:00
Stéphane Nicoll 7ffa12f90f Upgrade to Reactor 2023.0.13
Closes gh-34049
2024-12-10 14:09:57 +01:00
Stéphane Nicoll 0e74ffa144 Start building against Reactor 2023.0.13 snapshots
See gh-34049
2024-12-08 10:47:15 +01:00
Juergen Hoeller 357195289d Unit test for match against unresolvable wildcard
See gh-33982
2024-12-05 17:53:30 +01:00
Sébastien Deleuze 4fbca99f20 Remove unnecessary HandshakeHandlerRuntimeHints
Those hints are not needed anymore as of Spring Framework 6.1.
Backport of gh-34032.

Closes gh-34033
2024-12-05 15:46:31 +01:00
Juergen Hoeller c48ca5151f Upgrade to Gradle 8.11.1 2024-12-04 17:36:11 +01:00
Juergen Hoeller aaf2e8fbe6 Polishing
(cherry picked from commit edf7f3cd43)
2024-12-04 17:35:50 +01:00
Juergen Hoeller 3635ff0c17 Consistent fallback to NoUpgradeStrategyWebSocketService
Closes gh-33970

(cherry picked from commit 58c64cba2c)
2024-12-04 16:55:51 +01:00
Simon Baslé f20e76e227 Upgrade to Undertow 2.3.18.Final
Closes gh-33976
2024-11-27 14:31:46 +01:00
Simon Baslé dd32f95192 Dispatch in UndertowHttpHandlerAdapter
This ensures that the reactive handling of the request is dispatched
from the Undertow IO thread, marking the exchange as async rather than
ending it once the Undertow `handleRequest` method returns.

See gh-33885
Closes gh-33969
2024-11-26 16:58:21 +01:00
Tran Ngoc Nhan 75a920bc9f Fix a typo in the filters documentation
Backport of gh-33959
Closes gh-33971
2024-11-26 16:17:18 +01:00
Sam Brannen 92874adae9 Polish SpEL documentation
(cherry picked from commit 7196f3f554)
2024-11-18 11:43:14 +01:00
Sam Brannen 55167b7f50 Fix SpEL examples in reference guide
Closes gh-33907

(cherry picked from commit a12d40e10b)
2024-11-18 11:43:05 +01:00
Sam Brannen 3129afba19 Upgrade to Gradle 8.11
Closes gh-33895

(cherry picked from commit bb32df0a06)
2024-11-15 16:08:05 +01:00
Sam Brannen 1366d2926b Upgrade to antora-ui-spring version 0.4.18
Closes gh-33892

(cherry picked from commit fe8573c0ab)
2024-11-15 14:34:00 +01:00
9340 changed files with 44904 additions and 134714 deletions
+17
View File
@@ -0,0 +1,17 @@
<!--
!!! For Security Vulnerabilities, please go to https://spring.io/security-policy !!!
-->
**Affects:** \<Spring Framework version>
---
<!--
Thanks for taking the time to create an issue. Please read the following:
- Questions should be asked on Stack Overflow.
- For bugs, specify affected versions and explain what you are trying to do.
- For enhancements, provide context and describe the problem.
Issue or Pull Request? Create only one, not both. GitHub treats them as the same.
If unsure, start with an issue, and if you submit a pull request later, the
issue will be closed as superseded.
-->
-6
View File
@@ -1,6 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Community Support
url: https://stackoverflow.com/tags/spring
about: Please ask and answer questions on StackOverflow with the tag `spring`.
-23
View File
@@ -1,23 +0,0 @@
---
name: General
about: Bugs, enhancements, documentation, tasks.
title: ''
labels: ''
assignees: ''
---
<!--
Have you considered asking for help on stackoverflow.com?
** Bug Reports **
Please submit issues against OSS supported versions, see https://spring.io/projects/spring-framework#support
Please provide a minimal sample application that reproduces the problem for faster issue triage.
** Enhancements requests **
Before explaining how you would like things to work,
please describe a concrete use case for this feature and how you have tried to solve this so far.
-->
@@ -4,10 +4,6 @@ inputs:
milestone:
description: 'Name of the GitHub milestone for which a release will be created'
required: true
pre-release:
description: 'Whether the release is a pre-release (a milestone or release candidate)'
required: false
default: 'false'
token:
description: 'Token to use for authentication with GitHub'
required: true
@@ -24,4 +20,4 @@ runs:
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.token }}
run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md ${{ inputs.pre-release == 'true' && '--prerelease' || '' }}
run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md
@@ -24,18 +24,17 @@ runs:
using: composite
steps:
- name: Set Up Java
uses: actions/setup-java@v5
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@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
with:
cache-read-only: false
develocity-access-key: ${{ inputs.develocity-access-key }}
develocity-token-expiry: 4
- name: Configure Gradle Properties
shell: bash
run: |
@@ -1,15 +1,18 @@
name: Sync to Maven Central
description: 'Syncs a release to Maven Central and waits for it to be available for use'
inputs:
central-token-password:
description: 'Password for authentication with central.sonatype.com'
required: true
central-token-username:
description: 'Username for authentication with central.sonatype.com'
required: true
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
@@ -17,17 +20,23 @@ runs:
using: composite
steps:
- name: Set Up JFrog CLI
uses: jfrog/setup-jfrog-cli@5b06f730cc5a6f55d78b30753f8583454b08c0aa # v4.8.1
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/central-publish-action@0c03960e9b16fdfe70e2443e1d5393cbc3a35622 # v0.3.0
uses: spring-io/nexus-sync-action@42477a2230a2f694f9eaa4643fa9e76b99b7ab84 # v0.0.1
with:
token: ${{ inputs.central-token-password }}
token-name: ${{ inputs.central-token-username }}
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:
+2 -2
View File
@@ -18,9 +18,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v5
uses: actions/setup-java@v4
with:
distribution: 'liberica'
java-version: 17
@@ -2,7 +2,7 @@ name: Build and Deploy Snapshot
on:
push:
branches:
- 6.2.x
- 6.1.x
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
@@ -13,7 +13,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Check Out Code
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Build and Publish
id: build-and-publish
uses: ./.github/actions/build
@@ -21,13 +21,13 @@ jobs:
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
publish: true
- name: Deploy
uses: spring-io/artifactory-deploy-action@926d7f7cc810569395346bf3a4d91b380b3e355b # v0.0.4
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: 'spring-framework-6.2.x'
build-name: 'spring-framework-6.1.x'
folder: 'deployment-repository'
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
repository: 'libs-snapshot-local'
+16 -5
View File
@@ -9,17 +9,28 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Check Out Code
uses: actions/checkout@v6
- name: Set Up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'liberica'
java-version: '17'
- name: Check Out
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: Build
id: build
uses: ./.github/actions/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()
uses: ./.github/actions/print-jvm-thread-dumps
- name: Upload Build Reports
if: failure()
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v4
with:
name: build-reports
path: '**/build/reports/'
+5 -3
View File
@@ -2,7 +2,7 @@ name: CI
on:
push:
branches:
- 6.2.x
- 6.1.x
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
@@ -21,7 +21,9 @@ jobs:
toolchain: false
- version: 21
toolchain: true
- version: 25
- version: 22
toolchain: true
- version: 23
toolchain: true
exclude:
- os:
@@ -36,7 +38,7 @@ jobs:
git config --global core.longPaths true
Stop-Service -name Docker
- name: Check Out Code
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Build
id: build
uses: ./.github/actions/build
+2 -2
View File
@@ -15,11 +15,11 @@ permissions:
jobs:
build:
name: Dispatch docs deployment
if: github.repository_owner == 'spring-projects'
if: ${{ github.repository == 'spring-projects/spring-framework' }}
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
fetch-depth: 1
ref: docs-build
+9 -8
View File
@@ -2,7 +2,7 @@ name: Release
on:
push:
tags:
- v6.2.[0-9]+
- v6.1.[0-9]+
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Build and Publish
id: build-and-publish
uses: ./.github/actions/build
@@ -20,7 +20,7 @@ jobs:
develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
publish: true
- name: Stage Release
uses: spring-io/artifactory-deploy-action@926d7f7cc810569395346bf3a4d91b380b3e355b # v0.0.4
uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1
with:
artifact-properties: |
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
@@ -56,13 +56,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v6
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Sync to Maven Central
uses: ./.github/actions/sync-to-maven-central
with:
central-token-password: ${{ secrets.CENTRAL_TOKEN_PASSWORD }}
central-token-username: ${{ secrets.CENTRAL_TOKEN_USERNAME }}
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
@@ -72,7 +73,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up JFrog CLI
uses: jfrog/setup-jfrog-cli@5b06f730cc5a6f55d78b30753f8583454b08c0aa # v4.8.1
uses: jfrog/setup-jfrog-cli@9fe0f98bd45b19e6e931d457f4e98f8f84461fb5 # v4.4.1
env:
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
- name: Promote build
@@ -85,7 +86,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Code
uses: actions/checkout@v6
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Create GitHub Release
uses: ./.github/actions/create-github-release
with:
@@ -1,37 +0,0 @@
name: Update Antora UI Spring
on:
schedule:
- cron: '0 10 * * *' # Once per day at 10am UTC
workflow_dispatch:
permissions:
pull-requests: write
issues: write
contents: write
jobs:
update-antora-ui-spring:
name: Update on Supported Branches
if: ${{ github.repository == 'spring-projects/spring-framework' }}
runs-on: ubuntu-latest
strategy:
matrix:
branch: [ '6.1.x' ]
steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@5a57bcc6a0da2a1474136cf29571b277850432bc
name: Update
with:
docs-branch: ${{ matrix.branch }}
token: ${{ secrets.GITHUB_TOKEN }}
antora-file-path: 'framework-docs/antora-playbook.yml'
update-antora-ui-spring-docs-build:
name: Update on docs-build
if: ${{ github.repository == 'spring-projects/spring-framework' }}
runs-on: ubuntu-latest
steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@5a57bcc6a0da2a1474136cf29571b277850432bc
name: Update
with:
docs-branch: 'docs-build'
token: ${{ secrets.GITHUB_TOKEN }}
+5 -5
View File
@@ -30,23 +30,23 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Out Release Verification Tests
uses: actions/checkout@v6
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@v6
uses: actions/checkout@v4
with:
path: send-notification
sparse-checkout: .github/actions/send-notification
- name: Set Up Java
uses: actions/setup-java@v5
uses: actions/setup-java@v4
with:
distribution: 'liberica'
java-version: 17
- name: Set Up Gradle
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
with:
cache-read-only: false
- name: Configure Gradle Properties
@@ -64,7 +64,7 @@ jobs:
run: ./gradlew spring-framework-release-verification-tests:test
- name: Upload Build Reports on Failure
if: failure()
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v4
with:
name: build-reports
path: '**/build/reports/'
+1 -2
View File
@@ -42,8 +42,7 @@ spring-*/src/main/java/META-INF/MANIFEST.MF
*.iml
*.ipr
*.iws
.idea/*
!.idea/icon.svg
.idea
out
test-output
atlassian-ide-plugin.xml
-52
View File
@@ -1,52 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 510 510" style="enable-background:new 0 0 510 510;" xml:space="preserve">
<style type="text/css">
.st0{fill:#6DB33F;}
</style>
<title>icon-framework</title>
<g id="Layer_2_1_">
<g id="Layer_1-2">
<path class="st0" d="M479.2,162.5c-5.6-34.3-20.5-64.4-43.9-87.8S382,36.5,347.6,30.9C320.1,11.2,288.5,0,255,0
s-65.1,11.2-92.6,30.9C128,36.5,98,51.3,74.7,74.7C50.9,98.4,36.5,128.7,31,162.2C11.2,189.8,0,221.4,0,255s11.2,65.2,31,92.8
c5.5,33.5,19.9,63.8,43.7,87.5c23.3,23.3,53.3,38.2,87.7,43.8C190,498.8,221.5,510,255,510s65-11.2,92.6-30.9
c34.4-5.6,64.4-20.5,87.7-43.8c23.8-23.8,38.1-54.1,43.7-87.5c19.8-27.6,31-59.2,31-92.8S498.8,190,479.2,162.5z M473,224.2
c-0.8,6-1.9,11.9-3.2,17.9c-7.2-22.4-17.1-43.8-29.4-63.8c-5.5-22.8-13.6-44.9-24.4-65.8c22.9,14.8,42.2,32.6,56.7,52.6
C475.7,184.6,475.8,204.6,473,224.2z M444.7,199.6c9.3,17.7,16.6,36.3,21.9,55.6c-5.3,19.3-12.7,37.9-22,55.6
C450.9,274,451,236.4,444.7,199.6L444.7,199.6z M175.4,341.1c6.5-4.5,13.7-7.8,21.3-9.8c32.2-7.5,46.7-9.9,66.3-18.4
c36.8-15.9,74.8-53.3,84.6-94c-16.1,38.8-59.5,70.2-97.9,81.6c-26.2,7.8-79.8,13.6-80.1,13.6l-1.4-1.2
c-30.6-17.4-27-85.8,31.8-104.1c25.7-8,55.9-0.7,83.5-5.4c29.4-5,64.4-24.5,79.9-52c12.3,46.4,26.1,118.5-9.7,160.2
C309.4,363,211.8,318,175.4,341.1z M310.8,444.6c-17.7,9.4-36.5,16.8-55.8,22.1c-19.4-5.3-38.1-12.7-55.8-22.1
C236.1,450.9,273.9,450.9,310.8,444.6z M117.6,392.4c13.1,13.1,27.3,25.1,42.3,35.8c-19-5.9-37.2-13.8-54.5-23.7
c-9.9-17.3-17.8-35.7-23.7-54.7C92.4,365.1,104.5,379.3,117.6,392.4z M81.7,160.1c5.9-19,13.9-37.4,23.7-54.7
c17.3-9.8,35.5-17.8,54.5-23.7c-15.1,10.8-29.2,22.7-42.3,35.8C104.5,130.7,92.4,144.9,81.7,160.1z M149.1,346.4
c-8.5,0.2-15.5-6.5-15.7-15c-0.2-8.5,6.5-15.5,15-15.7c8.5-0.2,15.5,6.5,15.7,15c0,0,0,0.1,0,0.1
C164.2,339.3,157.5,346.2,149.1,346.4L149.1,346.4z M199.2,65.4c17.7-9.4,36.5-16.8,55.8-22.1c19.4,5.3,38.1,12.7,55.8,22.1
C273.9,59.1,236.1,59.1,199.2,65.4L199.2,65.4z M392.4,392.4c13.1-13.1,25.1-27.3,35.9-42.4c-5.9,19-13.9,37.3-23.7,54.5
c-17.3,9.8-35.5,17.7-54.5,23.7C365.2,417.5,379.3,405.5,392.4,392.4L392.4,392.4z M392.4,117.6c-13.1-13.1-27.2-25.1-42.3-35.8
c19,5.9,37.2,13.9,54.5,23.7c9.8,17.3,17.8,35.6,23.7,54.7C417.6,144.9,405.5,130.7,392.4,117.6L392.4,117.6z M430.5,79.5
c19.4,19.3,32.6,43.6,39.3,71.1c-16.2-19.2-36.6-36.2-60.4-50c-13.8-23.8-30.8-44.2-50-60.4C387.1,46.9,411.2,60.1,430.5,79.5
L430.5,79.5z M397.6,94c-20.8-10.7-42.8-18.8-65.6-24.2c-20-12.4-41.5-22.4-63.9-29.6c15.5-3.4,31.3-5.1,47.2-5.2
c10,0,19.9,0.8,29.7,2.3C365.1,51.8,382.8,71.1,397.6,94z M255,6.8c27.6,0,53.8,8,77.4,22.2c-5.6-0.5-11.3-0.7-17.1-0.7
c-20.4,0.1-40.6,2.8-60.3,8.1c-19.7-5.3-39.9-8-60.3-8.1c-5.8,0-11.5,0.2-17.1,0.7C201.2,14.7,227.4,6.8,255,6.8z M165,37.3
c9.8-1.5,19.8-2.3,29.7-2.3c15.9,0.1,31.7,1.8,47.2,5.2C219.5,47.4,198,57.3,178,69.8c-22.7,5.4-44.8,13.6-65.6,24.2
C127.2,71.1,144.9,51.8,165,37.3z M79.5,79.5c19.3-19.3,43.5-32.6,71.1-39.3c-19.2,16.2-36.2,36.6-50,60.4
c-23.7,13.8-44,30.6-60.2,49.8C47,123.4,59.8,99.1,79.5,79.5z M37.4,164.9c14.6-20,33.8-37.7,56.6-52.4
c-10.7,20.9-18.8,43-24.3,65.8c-12.3,20-22.2,41.4-29.4,63.8C34.6,215.4,33.5,189.3,37.4,164.9z M65.4,310.4
c-9.3-17.6-16.6-36.2-21.9-55.4c5.3-19.2,12.6-37.8,21.9-55.4C59.1,236.3,59.1,273.7,65.4,310.4L65.4,310.4z M29.2,332.8
c-14.4-23.7-22.4-50-22.4-77.8s8.1-54.1,22.4-77.8c-2.1,25,0.4,51.3,7.4,77.8C29.5,281.5,27,307.8,29.2,332.8z M40.3,268
c7.2,22.3,17.1,43.7,29.4,63.7c5.5,22.8,13.6,44.9,24.3,65.8c-22.8-14.7-42-32.5-56.6-52.4C33.5,320.7,34.6,294.6,40.3,268z
M79.5,430.5c-19.6-19.6-32.5-43.9-39.1-70.9c16.1,19.1,36.5,36,60.2,49.8c13.8,23.7,30.8,44.2,50,60.4
C122.9,463.1,98.8,449.9,79.5,430.5z M112.5,416c20.8,10.7,42.8,18.8,65.5,24.2c20,12.4,41.5,22.4,63.9,29.6
c-15.5,3.4-31.3,5.1-47.2,5.1c-10,0-19.9-0.8-29.7-2.3C144.9,458.1,127.2,438.9,112.5,416L112.5,416z M255,503.2
c-27.6,0-53.8-8-77.4-22.2c5.6,0.5,11.3,0.7,17.1,0.7c20.4-0.1,40.6-2.8,60.3-8.1c19.7,5.3,39.9,8,60.3,8.1
c5.8,0,11.5-0.2,17.1-0.7C308.8,495.3,282.6,503.2,255,503.2z M345,472.7c-9.8,1.5-19.8,2.3-29.7,2.3c-15.9-0.1-31.7-1.8-47.2-5.1
c22.4-7.2,43.9-17.2,63.9-29.6c22.7-5.4,44.8-13.6,65.6-24.2C382.8,438.9,365.1,458.1,345,472.7z M430.5,430.5
c-19.3,19.3-43.5,32.6-71.1,39.3c19.2-16.2,36.2-36.6,50-60.4c23.7-13.8,44-30.6,60.2-49.8C463.1,386.6,450.1,410.9,430.5,430.5
L430.5,430.5z M472.6,345.1c-14.6,20-33.8,37.7-56.6,52.4c10.7-20.8,18.8-42.8,24.3-65.6c12.3-20,22.2-41.3,29.5-63.7
C475.4,294.7,476.5,320.8,472.6,345.1L472.6,345.1z M473.5,255.3c2.6-9.9,4.7-20,6.1-30.1c2.2-15.7,2.7-31.7,1.4-47.5
c14.2,23.6,22.2,49.8,22.2,77.4s-8.1,54.1-22.4,77.8C482.9,307.9,480.5,281.6,473.5,255.3L473.5,255.3z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.8 KiB

+6 -7
View File
@@ -16,7 +16,7 @@ First off, thank you for taking the time to contribute! :+1: :tada:
### Code of Conduct
This project is governed by the [Spring Code of Conduct](https://github.com/spring-projects/spring-framework#coc-ov-file).
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.
@@ -65,6 +65,10 @@ follow-up reports will need to be created as new issues with a fresh description
#### Submit a Pull Request
1. If you have not previously done so, please sign the
[Contributor License Agreement](https://cla.spring.io/sign/spring). You will be reminded
automatically when you submit the PR.
1. Should you create an issue first? No, just create the pull request and use the
description to provide context and motivation, as you would for an issue. If you want
to start a discussion first or have already created an issue, once a pull request is
@@ -81,13 +85,8 @@ multiple edits or corrections of the same logical change. See
[Rewriting History section of Pro Git](https://git-scm.com/book/en/Git-Tools-Rewriting-History)
for an overview of streamlining the commit history.
1. All commits must include a _Signed-off-by_ trailer at the end of each commit message
to indicate that the contributor agrees to the Developer Certificate of Origin.
For additional details, please refer to the blog post
[Hello DCO, Goodbye CLA: Simplifying Contributions to Spring](https://spring.io/blog/2025/01/06/hello-dco-goodbye-cla-simplifying-contributions-to-spring).
1. Format commit messages using 55 characters for the subject line, 72 characters per line
for the description, followed by the issue fixed, for example, `Closes gh-22276`. See the
for the description, followed by the issue fixed, e.g. `Closes gh-22276`. See the
[Commit Guidelines section of Pro Git](https://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines)
for best practices around commit messages, and use `git log` to see some examples.
+3 -3
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=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)
# <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)
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](https://github.com/spring-projects/spring-framework/?tab=coc-ov-file#contributor-code-of-conduct). 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@spring.io.
## Access to Binaries
@@ -27,7 +27,7 @@ See the [Build from Source](https://github.com/spring-projects/spring-framework/
## Continuous Integration Builds
CI builds are defined with [GitHub Actions workflows](.github/workflows).
Information regarding CI builds can be found in the [Spring Framework Concourse pipeline](ci/README.adoc) documentation.
## Stay in Touch
+11 -5
View File
@@ -1,10 +1,16 @@
# Reporting a Vulnerability
Please, [open a draft security advisory](https://github.com/spring-projects/security-advisories/security/advisories/new) if you need to disclose and discuss a security issue in private with the Spring Framework team. Note that we only accept reports against [supported versions](https://spring.io/projects/spring-framework#support).
For more details, check out our [security policy](https://spring.io/security-policy).
# Security Policy
## JAR signing
Spring Framework JARs released on Maven Central are signed.
You'll find more information about the key here: https://spring.io/GPG-KEY-spring.txt
## Supported Versions
Please see the
[Spring Framework Versions](https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions)
wiki page.
## Reporting a Vulnerability
Please see https://spring.io/security-policy.
+15 -11
View File
@@ -2,14 +2,13 @@ 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.9.20'
id 'org.jetbrains.dokka' version '1.8.20'
id 'com.github.ben-manes.versions' version '0.51.0'
id 'com.github.bjornvester.xjc' version '1.8.2' 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 "4.1.0" apply false
}
ext {
@@ -25,17 +24,17 @@ configure(allprojects) { project ->
repositories {
mavenCentral()
maven {
url = "https://repo.spring.io/milestone"
url "https://repo.spring.io/milestone"
content {
// Netty 5 optional support
includeGroup 'io.projectreactor.netty'
}
}
if (version.contains('-')) {
maven { url = "https://repo.spring.io/milestone" }
maven { url "https://repo.spring.io/milestone" }
}
if (version.endsWith('-SNAPSHOT')) {
maven { url = "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/snapshot" }
}
}
configurations.all {
@@ -68,13 +67,17 @@ configure([rootProject] + javaProjects) { project ->
apply from: "${rootDir}/gradle/ide.gradle"
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.junit.platform:junit-platform-suite")
testImplementation("org.junit.jupiter:junit-jupiter-api")
testImplementation("org.junit.jupiter:junit-jupiter-params")
testImplementation("org.junit.platform:junit-platform-suite-api")
testImplementation("org.mockito:mockito-core")
testImplementation("org.mockito:mockito-junit-jupiter")
testImplementation("io.mockk:mockk")
testImplementation("org.assertj:assertj-core")
// Pull in the latest JUnit 5 Launcher API to ensure proper support in IDEs.
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testRuntimeOnly("org.junit.platform:junit-platform-suite-engine")
testRuntimeOnly("org.apache.logging.log4j:log4j-core")
testRuntimeOnly("org.apache.logging.log4j:log4j-jul")
testRuntimeOnly("org.apache.logging.log4j:log4j-slf4j2-impl")
@@ -86,19 +89,20 @@ 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.hibernate.org/orm/5.6/javadocs/",
"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.5.x/current/httpclient5/apidocs/",
"https://hc.apache.org/httpcomponents-client-5.4.x/current/httpclient5/apidocs/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
// TODO Uncomment link to JUnit 5 docs once we execute Gradle with Java 18+.
// See https://github.com/spring-projects/spring-framework/issues/27497
//
// "https://junit.org/junit5/docs/5.14.2/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.4-javadoc/",
// "https://junit.org/junit5/docs/5.10.4/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/",
// Previously there could be a split-package issue between JSR250 and JSR305 javax.annotation packages,
+1 -1
View File
@@ -6,7 +6,7 @@
<module name="io.spring.javaformat.checkstyle.check.SpringHeaderCheck">
<property name="fileExtensions" value="java"/>
<property name="headerType" value="apache2"/>
<property name="headerCopyrightPattern" value="20\d\d-present"/>
<property name="headerCopyrightPattern" value="20\d\d-20\d\d"/>
<property name="packageInfoHeaderType" value="none"/>
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.NewlineAtEndOfFileCheck"/>
+1 -1
View File
@@ -1,2 +1,2 @@
org.gradle.caching=true
javaFormatVersion=0.0.43
javaFormatVersion=0.0.42
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2025 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.
@@ -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.26.1");
checkstyle.setToolVersion("10.23.1");
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* Copyright 2002-2024 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.
@@ -21,8 +21,6 @@ import java.util.Map;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.tasks.testing.Test;
import org.gradle.api.tasks.testing.TestFrameworkOptions;
import org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions;
import org.gradle.testretry.TestRetryPlugin;
import org.gradle.testretry.TestRetryTaskExtension;
@@ -36,7 +34,6 @@ import org.gradle.testretry.TestRetryTaskExtension;
*
* @author Brian Clozel
* @author Andy Wilkinson
* @author Sam Brannen
*/
class TestConventions {
@@ -53,12 +50,7 @@ class TestConventions {
}
private void configureTests(Project project, Test test) {
TestFrameworkOptions existingOptions = test.getOptions();
test.useJUnitPlatform(options -> {
if (existingOptions instanceof JUnitPlatformOptions junitPlatformOptions) {
options.copyFrom(junitPlatformOptions);
}
});
test.useJUnitPlatform();
test.include("**/*Tests.class", "**/*Test.class");
test.setSystemProperties(Map.of(
"java.awt.headless", "true",
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
@@ -27,7 +27,6 @@ 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.TaskProvider;
import org.gradle.api.tasks.testing.Test;
import org.gradle.testing.base.TestingExtension;
@@ -53,7 +52,7 @@ public class RuntimeHintsAgentPlugin implements Plugin<Project> {
TestingExtension testing = project.getExtensions().getByType(TestingExtension.class);
JvmTestSuite jvmTestSuite = (JvmTestSuite) testing.getSuites().getByName("test");
RuntimeHintsAgentExtension agentExtension = createRuntimeHintsAgentExtension(project);
TaskProvider<Test> agentTest = project.getTasks().register(RUNTIMEHINTS_TEST_TASK, Test.class, test -> {
Test agentTest = project.getTasks().create(RUNTIMEHINTS_TEST_TASK, Test.class, test -> {
test.useJUnitPlatform(options -> {
options.includeTags("RuntimeHintsTests");
});
@@ -64,7 +63,7 @@ public class RuntimeHintsAgentPlugin implements Plugin<Project> {
test.setClasspath(jvmTestSuite.getSources().getRuntimeClasspath());
test.getJvmArgumentProviders().add(createRuntimeHintsAgentArgumentProvider(project, agentExtension));
});
project.getTasks().named("check", task -> task.dependsOn(agentTest));
project.getTasks().getByName("check", task -> task.dependsOn(agentTest));
project.getDependencies().add(CONFIGURATION_NAME, project.project(":spring-core-test"));
});
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present 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.
+5 -7
View File
@@ -1,6 +1,6 @@
plugins {
id 'java-platform'
id 'io.freefair.aggregate-javadoc' version '8.13.1'
id 'io.freefair.aggregate-javadoc' version '8.3'
}
description = "Spring Framework API Docs"
@@ -9,7 +9,7 @@ apply from: "${rootDir}/gradle/publications.gradle"
repositories {
maven {
url = "https://repo.spring.io/release"
url "https://repo.spring.io/release"
}
}
@@ -21,7 +21,6 @@ dependencies {
javadoc {
title = "${rootProject.description} ${version} API"
failOnError = true
options {
encoding = "UTF-8"
memberLevel = JavadocMemberLevel.PROTECTED
@@ -32,9 +31,8 @@ javadoc {
destinationDir = project.java.docsDir.dir("javadoc-api").get().asFile
splitIndex = true
links(rootProject.ext.javadocLinks)
// Check for 'syntax' and 'reference' during linting.
addBooleanOption('Xdoclint:syntax,reference', true)
addBooleanOption('Werror', true) // fail build on Javadoc warnings
addBooleanOption('Xdoclint:syntax,reference', true) // only check syntax and reference with doclint
addBooleanOption('Werror', true) // fail build on Javadoc warnings
}
maxMemory = "1024m"
doFirst {
@@ -89,7 +87,7 @@ tasks.register('schemaZip', Zip) {
archiveClassifier.set("schema")
description = "Builds -${archiveClassifier} archive containing all " +
"XSDs for deployment at https://springframework.org/schema."
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
duplicatesStrategy DuplicatesStrategy.EXCLUDE
moduleProjects.each { module ->
def Properties schemas = new Properties();
+2 -4
View File
@@ -13,9 +13,7 @@ content:
- url: https://github.com/spring-projects/spring-framework
# Refname matching:
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
# branches: We include snapshots for main, 6.2.x, and 7.0.x to 9.*.x.
branches: ['main', '6.2.x', '{7..9}.+({0..9}).x']
# tags: We effectively include all releases from 6.0.9 to 9.*.*.
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:
@@ -38,4 +36,4 @@ runtime:
failure_level: warn
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.25/ui-bundle.zip
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.18/ui-bundle.zip
+2 -4
View File
@@ -6,7 +6,7 @@ nav:
ext:
collector:
run:
command: gradlew -q -PbuildSrc.skipTests=true "-Dorg.gradle.jvmargs=-Xmx3g" :framework-docs:generateAntoraResources
command: gradlew -q -PbuildSrc.skipTests=true "-Dorg.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError" :framework-docs:generateAntoraResources
local: true
scan:
dir: ./build/generated-antora-resources
@@ -21,7 +21,6 @@ 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"
@@ -42,8 +41,7 @@ asciidoc:
spring-framework-reference: '{spring-framework-docs-root}/{spring-version}/reference'
#
# Other Spring portfolio projects
spring-boot-docs: '{docs-site}/spring-boot'
spring-boot-docs-ref: '{spring-boot-docs}/reference'
spring-boot-docs: '{docs-site}/spring-boot/docs/current/reference/html'
spring-boot-issues: '{spring-github-org}/spring-boot/issues'
# TODO add more projects / links or just build up on {docs-site}?
# TODO rename the below using new conventions
+12 -29
View File
@@ -37,39 +37,22 @@ javadoc {
repositories {
maven {
url = "https://repo.spring.io/release"
url "https://repo.spring.io/release"
}
}
dependencies {
implementation(project(":spring-aspects"))
implementation(project(":spring-context"))
implementation(project(":spring-context-support"))
implementation(project(":spring-core-test"))
implementation(project(":spring-jdbc"))
implementation(project(":spring-jms"))
implementation(project(":spring-test"))
implementation(project(":spring-web"))
implementation(project(":spring-webflux"))
implementation(project(":spring-webmvc"))
implementation(project(":spring-websocket"))
api(project(":spring-context"))
api(project(":spring-jdbc"))
api(project(":spring-jms"))
api(project(":spring-web"))
api(project(":spring-webflux"))
implementation("com.fasterxml.jackson.core:jackson-databind")
implementation("com.fasterxml.jackson.module:jackson-module-parameter-names")
implementation("com.mchange:c3p0:0.9.5.5")
implementation("com.oracle.database.jdbc:ojdbc11")
implementation("io.projectreactor.netty:reactor-netty-http")
implementation("jakarta.jms:jakarta.jms-api")
implementation("jakarta.servlet:jakarta.servlet-api")
implementation("jakarta.resource:jakarta.resource-api")
implementation("jakarta.validation:jakarta.validation-api")
implementation("jakarta.websocket:jakarta.websocket-client-api")
implementation("javax.cache:cache-api")
implementation("org.apache.activemq:activemq-ra:6.1.2")
implementation("org.apache.commons:commons-dbcp2:2.11.0")
implementation("org.aspectj:aspectjweaver")
api("com.oracle.database.jdbc:ojdbc11")
api("jakarta.jms:jakarta.jms-api")
api("jakarta.servlet:jakarta.servlet-api")
api("org.jetbrains.kotlin:kotlin-stdlib")
implementation(project(":spring-core-test"))
implementation("org.assertj:assertj-core")
implementation("org.eclipse.jetty.websocket:jetty-websocket-jetty-api")
implementation("org.jetbrains.kotlin:kotlin-stdlib")
implementation("org.junit.jupiter:junit-jupiter-api")
}
+83 -97
View File
@@ -60,7 +60,6 @@
**** xref:core/expressions/language-ref/constructors.adoc[]
**** xref:core/expressions/language-ref/variables.adoc[]
**** xref:core/expressions/language-ref/functions.adoc[]
**** xref:core/expressions/language-ref/varargs.adoc[]
**** xref:core/expressions/language-ref/bean-references.adoc[]
**** xref:core/expressions/language-ref/operator-ternary.adoc[]
**** xref:core/expressions/language-ref/operator-elvis.adoc[]
@@ -107,6 +106,87 @@
*** xref:core/appendix/xsd-schemas.adoc[]
*** xref:core/appendix/xml-custom.adoc[]
*** xref:core/appendix/application-startup-steps.adoc[]
* xref:testing.adoc[]
** xref:testing/introduction.adoc[]
** xref:testing/unit.adoc[]
** xref:testing/integration.adoc[]
** xref:testing/support-jdbc.adoc[]
** xref:testing/testcontext-framework.adoc[]
*** xref:testing/testcontext-framework/key-abstractions.adoc[]
*** xref:testing/testcontext-framework/bootstrapping.adoc[]
*** xref:testing/testcontext-framework/tel-config.adoc[]
*** xref:testing/testcontext-framework/application-events.adoc[]
*** xref:testing/testcontext-framework/test-execution-events.adoc[]
*** xref:testing/testcontext-framework/ctx-management.adoc[]
**** xref:testing/testcontext-framework/ctx-management/xml.adoc[]
**** xref:testing/testcontext-framework/ctx-management/groovy.adoc[]
**** xref:testing/testcontext-framework/ctx-management/javaconfig.adoc[]
**** xref:testing/testcontext-framework/ctx-management/mixed-config.adoc[]
**** xref:testing/testcontext-framework/ctx-management/context-customizers.adoc[]
**** xref:testing/testcontext-framework/ctx-management/initializers.adoc[]
**** xref:testing/testcontext-framework/ctx-management/inheritance.adoc[]
**** xref:testing/testcontext-framework/ctx-management/env-profiles.adoc[]
**** xref:testing/testcontext-framework/ctx-management/property-sources.adoc[]
**** xref:testing/testcontext-framework/ctx-management/dynamic-property-sources.adoc[]
**** xref:testing/testcontext-framework/ctx-management/web.adoc[]
**** xref:testing/testcontext-framework/ctx-management/web-mocks.adoc[]
**** xref:testing/testcontext-framework/ctx-management/caching.adoc[]
**** xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[]
**** xref:testing/testcontext-framework/ctx-management/hierarchies.adoc[]
*** xref:testing/testcontext-framework/fixture-di.adoc[]
*** xref:testing/testcontext-framework/web-scoped-beans.adoc[]
*** xref:testing/testcontext-framework/tx.adoc[]
*** xref:testing/testcontext-framework/executing-sql.adoc[]
*** xref:testing/testcontext-framework/parallel-test-execution.adoc[]
*** xref:testing/testcontext-framework/support-classes.adoc[]
*** xref:testing/testcontext-framework/aot.adoc[]
** xref:testing/webtestclient.adoc[]
** xref:testing/spring-mvc-test-framework.adoc[]
*** xref:testing/spring-mvc-test-framework/server.adoc[]
*** xref:testing/spring-mvc-test-framework/server-static-imports.adoc[]
*** xref:testing/spring-mvc-test-framework/server-setup-options.adoc[]
*** xref:testing/spring-mvc-test-framework/server-setup-steps.adoc[]
*** xref:testing/spring-mvc-test-framework/server-performing-requests.adoc[]
*** xref:testing/spring-mvc-test-framework/server-defining-expectations.adoc[]
*** xref:testing/spring-mvc-test-framework/async-requests.adoc[]
*** xref:testing/spring-mvc-test-framework/vs-streaming-response.adoc[]
*** xref:testing/spring-mvc-test-framework/server-filters.adoc[]
*** xref:testing/spring-mvc-test-framework/vs-end-to-end-integration-tests.adoc[]
*** xref:testing/spring-mvc-test-framework/server-resources.adoc[]
*** xref:testing/spring-mvc-test-framework/server-htmlunit.adoc[]
**** xref:testing/spring-mvc-test-framework/server-htmlunit/why.adoc[]
**** xref:testing/spring-mvc-test-framework/server-htmlunit/mah.adoc[]
**** xref:testing/spring-mvc-test-framework/server-htmlunit/webdriver.adoc[]
**** xref:testing/spring-mvc-test-framework/server-htmlunit/geb.adoc[]
** xref:testing/spring-mvc-test-client.adoc[]
** xref:testing/appendix.adoc[]
*** xref:testing/annotations.adoc[]
**** xref:testing/annotations/integration-standard.adoc[]
**** xref:testing/annotations/integration-spring.adoc[]
***** xref:testing/annotations/integration-spring/annotation-bootstrapwith.adoc[]
***** xref:testing/annotations/integration-spring/annotation-contextconfiguration.adoc[]
***** xref:testing/annotations/integration-spring/annotation-webappconfiguration.adoc[]
***** xref:testing/annotations/integration-spring/annotation-contexthierarchy.adoc[]
***** xref:testing/annotations/integration-spring/annotation-contextcustomizerfactories.adoc[]
***** xref:testing/annotations/integration-spring/annotation-activeprofiles.adoc[]
***** xref:testing/annotations/integration-spring/annotation-testpropertysource.adoc[]
***** xref:testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc[]
***** xref:testing/annotations/integration-spring/annotation-dirtiescontext.adoc[]
***** xref:testing/annotations/integration-spring/annotation-testexecutionlisteners.adoc[]
***** xref:testing/annotations/integration-spring/annotation-recordapplicationevents.adoc[]
***** xref:testing/annotations/integration-spring/annotation-commit.adoc[]
***** xref:testing/annotations/integration-spring/annotation-rollback.adoc[]
***** xref:testing/annotations/integration-spring/annotation-beforetransaction.adoc[]
***** xref:testing/annotations/integration-spring/annotation-aftertransaction.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sql.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sqlconfig.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sqlmergemode.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sqlgroup.adoc[]
***** xref:testing/annotations/integration-spring/annotation-disabledinaotmode.adoc[]
**** xref:testing/annotations/integration-junit4.adoc[]
**** xref:testing/annotations/integration-junit-jupiter.adoc[]
**** xref:testing/annotations/integration-meta.adoc[]
*** xref:testing/resources.adoc[]
* xref:data-access.adoc[]
** xref:data-access/transaction.adoc[]
*** xref:data-access/transaction/motivation.adoc[]
@@ -165,7 +245,6 @@
**** xref:web/webmvc/mvc-servlet/multipart.adoc[]
**** xref:web/webmvc/mvc-servlet/logging.adoc[]
*** xref:web/webmvc/filters.adoc[]
*** xref:web/webmvc/message-converters.adoc[]
*** xref:web/webmvc/mvc-controller.adoc[]
**** xref:web/webmvc/mvc-controller/ann.adoc[]
**** xref:web/webmvc/mvc-controller/ann-requestmapping.adoc[]
@@ -206,7 +285,6 @@
**** xref:web/webmvc-view/mvc-freemarker.adoc[]
**** xref:web/webmvc-view/mvc-groovymarkup.adoc[]
**** xref:web/webmvc-view/mvc-script.adoc[]
**** xref:web/webmvc-view/mvc-fragments.adoc[]
**** xref:web/webmvc-view/mvc-jsp.adoc[]
**** xref:web/webmvc-view/mvc-feeds.adoc[]
**** xref:web/webmvc-view/mvc-document.adoc[]
@@ -314,97 +392,6 @@
** xref:web/webflux-test.adoc[]
** xref:rsocket.adoc[]
** xref:web/webflux-reactive-libraries.adoc[]
* xref:testing.adoc[]
** xref:testing/introduction.adoc[]
** xref:testing/unit.adoc[]
** xref:testing/integration.adoc[]
** xref:testing/support-jdbc.adoc[]
** xref:testing/testcontext-framework.adoc[]
*** xref:testing/testcontext-framework/key-abstractions.adoc[]
*** xref:testing/testcontext-framework/bootstrapping.adoc[]
*** xref:testing/testcontext-framework/tel-config.adoc[]
*** xref:testing/testcontext-framework/application-events.adoc[]
*** xref:testing/testcontext-framework/test-execution-events.adoc[]
*** xref:testing/testcontext-framework/ctx-management.adoc[]
**** xref:testing/testcontext-framework/ctx-management/xml.adoc[]
**** xref:testing/testcontext-framework/ctx-management/groovy.adoc[]
**** xref:testing/testcontext-framework/ctx-management/javaconfig.adoc[]
**** xref:testing/testcontext-framework/ctx-management/mixed-config.adoc[]
**** xref:testing/testcontext-framework/ctx-management/context-customizers.adoc[]
**** xref:testing/testcontext-framework/ctx-management/initializers.adoc[]
**** xref:testing/testcontext-framework/ctx-management/inheritance.adoc[]
**** xref:testing/testcontext-framework/ctx-management/env-profiles.adoc[]
**** xref:testing/testcontext-framework/ctx-management/property-sources.adoc[]
**** xref:testing/testcontext-framework/ctx-management/dynamic-property-sources.adoc[]
**** xref:testing/testcontext-framework/ctx-management/web.adoc[]
**** xref:testing/testcontext-framework/ctx-management/web-mocks.adoc[]
**** xref:testing/testcontext-framework/ctx-management/caching.adoc[]
**** xref:testing/testcontext-framework/ctx-management/failure-threshold.adoc[]
**** xref:testing/testcontext-framework/ctx-management/hierarchies.adoc[]
*** xref:testing/testcontext-framework/fixture-di.adoc[]
*** xref:testing/testcontext-framework/bean-overriding.adoc[]
*** xref:testing/testcontext-framework/web-scoped-beans.adoc[]
*** xref:testing/testcontext-framework/tx.adoc[]
*** xref:testing/testcontext-framework/executing-sql.adoc[]
*** xref:testing/testcontext-framework/parallel-test-execution.adoc[]
*** xref:testing/testcontext-framework/support-classes.adoc[]
*** xref:testing/testcontext-framework/aot.adoc[]
** xref:testing/webtestclient.adoc[]
** xref:testing/mockmvc.adoc[]
*** xref:testing/mockmvc/overview.adoc[]
*** xref:testing/mockmvc/setup-options.adoc[]
*** xref:testing/mockmvc/hamcrest.adoc[]
**** xref:testing/mockmvc/hamcrest/static-imports.adoc[]
**** xref:testing/mockmvc/hamcrest/setup.adoc[]
**** xref:testing/mockmvc/hamcrest/setup-steps.adoc[]
**** xref:testing/mockmvc/hamcrest/requests.adoc[]
**** xref:testing/mockmvc/hamcrest/expectations.adoc[]
**** xref:testing/mockmvc/hamcrest/async-requests.adoc[]
**** xref:testing/mockmvc/hamcrest/vs-streaming-response.adoc[]
**** xref:testing/mockmvc/hamcrest/filters.adoc[]
*** xref:testing/mockmvc/assertj.adoc[]
**** xref:testing/mockmvc/assertj/setup.adoc[]
**** xref:testing/mockmvc/assertj/requests.adoc[]
**** xref:testing/mockmvc/assertj/assertions.adoc[]
**** xref:testing/mockmvc/assertj/integration.adoc[]
*** xref:testing/mockmvc/htmlunit.adoc[]
**** xref:testing/mockmvc/htmlunit/why.adoc[]
**** xref:testing/mockmvc/htmlunit/mah.adoc[]
**** xref:testing/mockmvc/htmlunit/webdriver.adoc[]
**** xref:testing/mockmvc/htmlunit/geb.adoc[]
*** xref:testing/mockmvc/vs-end-to-end-integration-tests.adoc[]
*** xref:testing/mockmvc/resources.adoc[]
** xref:testing/spring-mvc-test-client.adoc[]
** xref:testing/appendix.adoc[]
*** xref:testing/annotations.adoc[]
**** xref:testing/annotations/integration-standard.adoc[]
**** xref:testing/annotations/integration-spring.adoc[]
***** xref:testing/annotations/integration-spring/annotation-bootstrapwith.adoc[]
***** xref:testing/annotations/integration-spring/annotation-contextconfiguration.adoc[]
***** xref:testing/annotations/integration-spring/annotation-webappconfiguration.adoc[]
***** xref:testing/annotations/integration-spring/annotation-contexthierarchy.adoc[]
***** xref:testing/annotations/integration-spring/annotation-contextcustomizerfactories.adoc[]
***** xref:testing/annotations/integration-spring/annotation-activeprofiles.adoc[]
***** xref:testing/annotations/integration-spring/annotation-testpropertysource.adoc[]
***** xref:testing/annotations/integration-spring/annotation-dynamicpropertysource.adoc[]
***** 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[]
***** xref:testing/annotations/integration-spring/annotation-commit.adoc[]
***** xref:testing/annotations/integration-spring/annotation-rollback.adoc[]
***** xref:testing/annotations/integration-spring/annotation-beforetransaction.adoc[]
***** xref:testing/annotations/integration-spring/annotation-aftertransaction.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sql.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sqlconfig.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sqlmergemode.adoc[]
***** xref:testing/annotations/integration-spring/annotation-sqlgroup.adoc[]
***** xref:testing/annotations/integration-spring/annotation-disabledinaotmode.adoc[]
**** xref:testing/annotations/integration-junit4.adoc[]
**** xref:testing/annotations/integration-junit-jupiter.adoc[]
**** xref:testing/annotations/integration-meta.adoc[]
*** xref:testing/resources.adoc[]
* xref:integration.adoc[]
** xref:integration/rest-clients.adoc[]
** xref:integration/jms.adoc[]
@@ -452,6 +439,5 @@
** xref:languages/groovy.adoc[]
** xref:languages/dynamic.adoc[]
* xref:appendix.adoc[]
* {spring-framework-docs-root}/{spring-version}/javadoc-api/[Java API,window=_blank, role=link-external]
* {spring-framework-api-kdoc}/[Kotlin API,window=_blank, role=link-external]
* {spring-framework-wiki}[Wiki, window=_blank, role=link-external]
* {spring-framework-wiki}[Wiki]
@@ -92,25 +92,11 @@ the repeated JNDI lookup overhead. See
{spring-framework-api}++/jndi/JndiLocatorDelegate.html#IGNORE_JNDI_PROPERTY_NAME++[`JndiLocatorDelegate`]
for details.
| `spring.locking.strict`
| Instructs Spring to enforce strict locking during bean creation, rather than the mix of
strict and lenient locking that 6.2 applies by default. See
{spring-framework-api}++/beans/factory/support/DefaultListableBeanFactory.html#STRICT_LOCKING_PROPERTY_NAME++[`DefaultListableBeanFactory`]
for details.
| `spring.objenesis.ignore`
| Instructs Spring to ignore Objenesis, not even attempting to use it. See
{spring-framework-api}++/objenesis/SpringObjenesis.html#IGNORE_OBJENESIS_PROPERTY_NAME++[`SpringObjenesis`]
for details.
| `spring.placeholder.escapeCharacter.default`
| The default escape character for property placeholder support. If not set, `'\'` will
be used. Can be set to a custom escape character or an empty string to disable support
for an escape character. The default escape character be explicitly overridden in
`PropertySourcesPlaceholderConfigurer` and subclasses of `AbstractPropertyResolver`. See
{spring-framework-api}++/core/env/AbstractPropertyResolver.html#DEFAULT_PLACEHOLDER_ESCAPE_CHARACTER_PROPERTY_NAME++[`AbstractPropertyResolver`]
for details.
| `spring.test.aot.processing.failOnError`
| A boolean flag that controls whether errors encountered during AOT processing in the
_Spring TestContext Framework_ should result in an exception that fails the overall process.
@@ -17,3 +17,14 @@ is also provided.
AOT processing can be used to optimize your application ahead-of-time. It is typically
used for native image deployment using GraalVM.
@@ -6,3 +6,7 @@ The previous chapter described the Spring's support for AOP with @AspectJ and sc
aspect definitions. In this chapter, we discuss the lower-level Spring AOP APIs. For common
applications, we recommend the use of Spring AOP with AspectJ pointcuts as described in the
previous chapter.
@@ -4,6 +4,7 @@
Now we can examine how Spring AOP handles advice.
[[aop-api-advice-lifecycle]]
== Advice Lifecycles
@@ -21,20 +22,22 @@ the advice adds state to the proxied object.
You can use a mix of shared and per-instance advice in the same AOP proxy.
[[aop-api-advice-types]]
== Advice Types in Spring
Spring provides several advice types and is extensible to support
arbitrary advice types. This section describes the basic concepts and standard advice types.
[[aop-api-advice-around]]
=== Interception Around Advice
The most fundamental advice type in Spring is _interception around advice_.
The most fundamental advice type in Spring is interception around advice.
Spring is compliant with the AOP Alliance interface for around advice that uses method
interception. Classes that implement around advice should therefore implement the
following `MethodInterceptor` interface from the `org.aopalliance.intercept` package:
Spring is compliant with the AOP `Alliance` interface for around advice that uses method
interception. Classes that implement `MethodInterceptor` and that implement around advice should also implement the
following interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -46,8 +49,8 @@ following `MethodInterceptor` interface from the `org.aopalliance.intercept` pac
The `MethodInvocation` argument to the `invoke()` method exposes the method being
invoked, the target join point, the AOP proxy, and the arguments to the method. The
`invoke()` method should return the invocation's result: typically the return value of
the join point.
`invoke()` method should return the invocation's result: the return value of the join
point.
The following example shows a simple `MethodInterceptor` implementation:
@@ -55,30 +58,30 @@ The following example shows a simple `MethodInterceptor` implementation:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class DebugInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Before: invocation=[" + invocation + "]");
Object result = invocation.proceed();
Object rval = invocation.proceed();
System.out.println("Invocation returned");
return result;
return rval;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class DebugInterceptor : MethodInterceptor {
override fun invoke(invocation: MethodInvocation): Any {
println("Before: invocation=[$invocation]")
val result = invocation.proceed()
val rval = invocation.proceed()
println("Invocation returned")
return result
return rval
}
}
----
@@ -98,10 +101,11 @@ you are likely to want to run the aspect in another AOP framework. Note that poi
are not currently interoperable between frameworks, and the AOP Alliance does not
currently define pointcut interfaces.
[[aop-api-advice-before]]
=== Before Advice
A simpler advice type is a _before advice_. This does not need a `MethodInvocation`
A simpler advice type is a before advice. This does not need a `MethodInvocation`
object, since it is called only before entering the method.
The main advantage of a before advice is that there is no need to invoke the `proceed()`
@@ -118,6 +122,10 @@ The following listing shows the `MethodBeforeAdvice` interface:
}
----
(Spring's API design would allow for
field before advice, although the usual objects apply to field interception and it is
unlikely for Spring to ever implement it.)
Note that the return type is `void`. Before advice can insert custom behavior before the join
point runs but cannot change the return value. If a before advice throws an
exception, it stops further execution of the interceptor chain. The exception
@@ -131,7 +139,7 @@ The following example shows a before advice in Spring, which counts all method i
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class CountingBeforeAdvice implements MethodBeforeAdvice {
@@ -149,7 +157,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class CountingBeforeAdvice : MethodBeforeAdvice {
@@ -164,13 +172,14 @@ Kotlin::
TIP: Before advice can be used with any pointcut.
[[aop-api-advice-throws]]
=== Throws Advice
_Throws advice_ is invoked after the return of the join point if the join point threw
Throws advice is invoked after the return of the join point if the join point threw
an exception. Spring offers typed throws advice. Note that this means that the
`org.springframework.aop.ThrowsAdvice` interface does not contain any methods. It is a
marker interface identifying that the given object implements one or more typed throws
tag interface identifying that the given object implements one or more typed throws
advice methods. These should be in the following form:
[source,java,indent=0,subs="verbatim,quotes"]
@@ -180,16 +189,15 @@ advice methods. These should be in the following form:
Only the last argument is required. The method signatures may have either one or four
arguments, depending on whether the advice method is interested in the method and
arguments. The next two listings show classes that are examples of throws advice.
arguments. The next two listing show classes that are examples of throws advice.
The following advice is invoked if a `RemoteException` is thrown (including subclasses of
`RemoteException`):
The following advice is invoked if a `RemoteException` is thrown (including from subclasses):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class RemoteThrowsAdvice implements ThrowsAdvice {
@@ -201,7 +209,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class RemoteThrowsAdvice : ThrowsAdvice {
@@ -212,15 +220,15 @@ Kotlin::
----
======
Unlike the preceding advice, the next example declares four arguments, so that it has
access to the invoked method, method arguments, and target object. The following advice
is invoked if a `ServletException` is thrown:
Unlike the preceding
advice, the next example declares four arguments, so that it has access to the invoked method, method
arguments, and target object. The following advice is invoked if a `ServletException` is thrown:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {
@@ -232,7 +240,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class ServletThrowsAdviceWithArguments : ThrowsAdvice {
@@ -251,7 +259,7 @@ methods can be combined in a single class. The following listing shows the final
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public static class CombinedThrowsAdvice implements ThrowsAdvice {
@@ -267,7 +275,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class CombinedThrowsAdvice : ThrowsAdvice {
@@ -292,10 +300,11 @@ exception that is incompatible with the target method's signature!_
TIP: Throws advice can be used with any pointcut.
[[aop-api-advice-after-returning]]
=== After Returning Advice
An _after returning advice_ in Spring must implement the
An after returning advice in Spring must implement the
`org.springframework.aop.AfterReturningAdvice` interface, which the following listing shows:
[source,java,indent=0,subs="verbatim,quotes"]
@@ -317,7 +326,7 @@ not thrown exceptions:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class CountingAfterReturningAdvice implements AfterReturningAdvice {
@@ -336,7 +345,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class CountingAfterReturningAdvice : AfterReturningAdvice {
@@ -355,10 +364,11 @@ thrown up the interceptor chain instead of the return value.
TIP: After returning advice can be used with any pointcut.
[[aop-api-advice-introduction]]
=== Introduction Advice
Spring treats _introduction advice_ as a special kind of interception advice.
Spring treats introduction advice as a special kind of interception advice.
Introduction requires an `IntroductionAdvisor` and an `IntroductionInterceptor` that
implement the following interface:
@@ -410,7 +420,7 @@ introduce the following interface to one or more objects:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public interface Lockable {
void lock();
@@ -421,7 +431,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
interface Lockable {
fun lock()
@@ -470,7 +480,7 @@ The following example shows the example `LockMixin` class:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {
@@ -494,12 +504,13 @@ Java::
}
return super.invoke(invocation);
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class LockMixin : DelegatingIntroductionInterceptor(), Lockable {
@@ -523,6 +534,7 @@ Kotlin::
}
return super.invoke(invocation)
}
}
----
======
@@ -544,7 +556,7 @@ The following example shows our `LockMixinAdvisor` class:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
@@ -556,7 +568,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class LockMixinAdvisor : DefaultIntroductionAdvisor(LockMixin(), Lockable::class.java)
----
@@ -573,3 +585,8 @@ We can apply this advisor programmatically by using the `Advised.addAdvisor()` m
(the recommended way) in XML configuration, as any other advisor. All proxy creation
choices discussed below, including "`auto proxy creators,`" correctly handle introductions
and stateful mixins.
@@ -10,7 +10,7 @@ following methods:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Advisor[] getAdvisors();
@@ -35,7 +35,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
fun getAdvisors(): Array<Advisor>
@@ -90,7 +90,7 @@ manipulating its advice:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Advised advised = (Advised) myObject;
Advisor[] advisors = advised.getAdvisors();
@@ -110,7 +110,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val advised = myObject as Advised
val advisors = advised.advisors
@@ -142,3 +142,7 @@ case, the `Advised` `isFrozen()` method returns `true`, and any attempts to modi
advice through addition or removal results in an `AopConfigException`. The ability
to freeze the state of an advised object is useful in some cases (for example, to
prevent calling code removing a security interceptor).
@@ -14,3 +14,7 @@ It is possible to mix advisor and advice types in Spring in the same AOP proxy.
example, you could use an interception around advice, throws advice, and before advice in
one proxy configuration. Spring automatically creates the necessary interceptor
chain.
@@ -19,12 +19,14 @@ There are two ways to do this:
auto-proxy creation driven by source-level metadata attributes.
[[aop-autoproxy-choices]]
== Auto-proxy Bean Definitions
This section covers the auto-proxy creators provided by the
`org.springframework.aop.framework.autoproxy` package.
[[aop-api-autoproxy]]
=== `BeanNameAutoProxyCreator`
@@ -59,6 +61,7 @@ automatically created by the `BeanNameAutoProxyCreator`. The same advice is appl
to all matching beans. Note that, if advisors are used (rather than the interceptor in
the preceding example), the pointcuts may apply differently to different beans.
[[aop-api-autoproxy-default]]
=== `DefaultAdvisorAutoProxyCreator`
@@ -122,3 +125,7 @@ differently configured, AdvisorAutoProxyCreators in the same factory) and orderi
Advisors can implement the `org.springframework.core.Ordered` interface to ensure
correct ordering if this is an issue. The `TransactionAttributeSourceAdvisor` used in the
preceding example has a configurable order value. The default setting is unordered.
@@ -65,3 +65,7 @@ that, if you have a (parent) bean definition that you intend to use only as a te
and this definition specifies a class, you must make sure to set the `abstract`
attribute to `true`. Otherwise, the application context actually tries to
pre-instantiate it.
@@ -14,6 +14,7 @@ the pointcuts, any advice that applies, and their ordering. However, there are s
options that are preferable if you do not need such control.
[[aop-pfb-1]]
== Basics
@@ -31,6 +32,7 @@ application objects (besides the target, which should be available in any AOP
framework), benefiting from all the pluggability provided by Dependency Injection.
[[aop-pfb-2]]
== JavaBean Properties
@@ -85,6 +87,7 @@ to be applied. You can find an example of using this feature in xref:core/aop-ap
`false`.
[[aop-pfb-proxy-types]]
== JDK- and CGLIB-based proxies
@@ -134,6 +137,7 @@ interface that the target class implements to the `proxyInterfaces` property. Ho
it is significantly less work and less prone to typographical errors.
[[aop-api-proxying-intf]]
== Proxying Interfaces
@@ -192,16 +196,16 @@ follows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Person person = (Person) factory.getBean("person");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val person = factory.getBean("person") as Person
val person = factory.getBean("person") as Person;
----
======
@@ -259,6 +263,7 @@ However, there are times when being able to obtain the un-advised target from th
factory might actually be an advantage (for example, in certain test scenarios).
[[aop-api-proxying-class]]
== Proxying Classes
@@ -297,6 +302,7 @@ There is little performance difference between CGLIB proxies and dynamic proxies
Performance should not be a decisive consideration in this case.
[[aop-global-advisors]]
== Using "`Global`" Advisors
@@ -319,3 +325,7 @@ two global advisors:
<bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>
----
@@ -4,6 +4,7 @@
This section describes how Spring handles the crucial pointcut concept.
[[aop-api-concepts]]
== Concepts
@@ -68,6 +69,7 @@ TIP: If possible, try to make pointcuts static, allowing the AOP framework to ca
results of pointcut evaluation when an AOP proxy is created.
[[aop-api-pointcut-ops]]
== Operations on Pointcuts
@@ -82,6 +84,7 @@ You can compose pointcuts by using the static methods in the
expressions is usually a simpler approach.
[[aop-api-pointcuts-aspectj]]
== AspectJ Expression Pointcuts
@@ -92,12 +95,14 @@ uses an AspectJ-supplied library to parse an AspectJ pointcut expression string.
See the xref:core/aop.adoc[previous chapter] for a discussion of supported AspectJ pointcut primitives.
[[aop-api-pointcuts-impls]]
== Convenience Pointcut Implementations
Spring provides several convenient pointcut implementations. You can use some of them
directly; others are intended to be subclassed in application-specific pointcuts.
[[aop-api-pointcuts-static]]
=== Static Pointcuts
@@ -123,7 +128,18 @@ the resulting pointcut is effectively the union of the specified patterns.)
The following example shows how to use `JdkRegexpMethodPointcut`:
include-code::./JdkRegexpConfiguration[tag=snippet,indent=0]
[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>
----
Spring provides a convenience class named `RegexpMethodPointcutAdvisor`, which lets us
also reference an `Advice` (remember that an `Advice` can be an interceptor, before advice,
@@ -131,7 +147,21 @@ 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:
include-code::./RegexpConfiguration[tag=snippet,indent=0]
[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>
----
You can use `RegexpMethodPointcutAdvisor` with any `Advice` type.
@@ -141,6 +171,7 @@ You can use `RegexpMethodPointcutAdvisor` with any `Advice` type.
An important type of static pointcut is a metadata-driven pointcut. This uses the
values of metadata attributes (typically, source-level metadata).
[[aop-api-pointcuts-dynamic]]
=== Dynamic pointcuts
@@ -166,6 +197,7 @@ other dynamic pointcuts. In Java 1.4, the cost is about five times that of other
pointcuts.
[[aop-api-pointcuts-superclasses]]
== Pointcut Superclasses
@@ -180,7 +212,7 @@ following example shows how to subclass `StaticMethodMatcherPointcut`:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class TestStaticPointcut extends StaticMethodMatcherPointcut {
@@ -192,7 +224,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class TestStaticPointcut : StaticMethodMatcherPointcut() {
@@ -207,6 +239,7 @@ There are also superclasses for dynamic pointcuts.
You can use custom pointcuts with any advice type.
[[aop-api-pointcuts-custom]]
== Custom Pointcuts
@@ -217,3 +250,7 @@ expression language, if you can.
NOTE: Later versions of Spring may offer support for "`semantic pointcuts`" as offered by JAC --
for example, "`all methods that change instance variables in the target object.`"
@@ -12,7 +12,7 @@ interceptor and one advisor:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addAdvice(myMethodInterceptor);
@@ -22,7 +22,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val factory = ProxyFactory(myBusinessInterfaceImpl)
factory.addAdvice(myMethodInterceptor)
@@ -22,6 +22,7 @@ rather than a singleton bean definition. This allows Spring to create a new targ
instance when required.
[[aop-ts-swap]]
== Hot-swappable Target Sources
@@ -37,7 +38,7 @@ You can change the target by using the `swap()` method on HotSwappableTargetSour
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget);
@@ -45,7 +46,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val swapper = beanFactory.getBean("swapper") as HotSwappableTargetSource
val oldTarget = swapper.swap(newTarget)
@@ -76,6 +77,7 @@ use a `TargetSource`), any `TargetSource` can be used in conjunction with
arbitrary advice.
[[aop-ts-pool]]
== Pooling Target Sources
@@ -150,7 +152,7 @@ The cast is defined as follows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
System.out.println("Max pool size is " + conf.getMaxSize());
@@ -158,7 +160,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val conf = beanFactory.getBean("businessObject") as PoolingConfig
println("Max pool size is " + conf.maxSize)
@@ -173,6 +175,7 @@ Simpler pooling is available by using auto-proxying. You can set the `TargetSour
used by any auto-proxy creator.
[[aop-ts-prototype]]
== Prototype Target Sources
@@ -197,6 +200,7 @@ The only property is the name of the target bean. Inheritance is used in the
source, the target bean must be a prototype bean definition.
[[aop-ts-threadlocal]]
== `ThreadLocal` Target Sources
@@ -222,3 +226,7 @@ always remember to correctly set and unset (where the latter involves a call to
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.
@@ -32,3 +32,7 @@ AOP is used in the Spring Framework to:
NOTE: If you are interested only in generic declarative services or other pre-packaged
declarative middleware services such as pooling, you do not need to work directly with
Spring AOP, and can skip most of this chapter.
@@ -15,7 +15,7 @@ The basic usage for this class is very simple, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
// create a factory that can generate a proxy for the given target object
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);
@@ -34,7 +34,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
// create a factory that can generate a proxy for the given target object
val factory = AspectJProxyFactory(targetObject)
@@ -53,3 +53,7 @@ Kotlin::
======
See the {spring-framework-api}/aop/aspectj/annotation/AspectJProxyFactory.html[javadoc] for more information.
@@ -11,3 +11,6 @@ there is no dependency on the AspectJ compiler or weaver.
NOTE: Using the AspectJ compiler and weaver enables use of the full AspectJ language and
is discussed in xref:core/aop/using-aspectj.adoc[Using AspectJ with Spring Applications].
@@ -17,7 +17,7 @@ The following example uses an inline pointcut expression.
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@@ -34,7 +34,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
@@ -57,7 +57,7 @@ as follows:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@@ -74,7 +74,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
@@ -101,7 +101,7 @@ You can declare it by using the `@AfterReturning` annotation.
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@@ -118,7 +118,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterReturning
@@ -146,7 +146,7 @@ access, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@@ -165,7 +165,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterReturning
@@ -204,7 +204,7 @@ following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@@ -221,7 +221,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterThrowing
@@ -247,7 +247,7 @@ The following example shows how to do so:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@@ -266,7 +266,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterThrowing
@@ -311,7 +311,7 @@ purposes. The following example shows how to use after finally advice:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;
@@ -328,7 +328,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.After
@@ -417,7 +417,7 @@ The following example shows how to use around advice:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
@@ -438,7 +438,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Around
@@ -500,7 +500,7 @@ You could write the following:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
public void validateAccount(Account account) {
@@ -510,7 +510,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
fun validateAccount(account: Account) {
@@ -533,7 +533,7 @@ from the advice. This would look as follows:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Pointcut("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
private void accountDataAccessOperation(Account account) {}
@@ -546,7 +546,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Pointcut("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
private fun accountDataAccessOperation(account: Account) {
@@ -572,7 +572,7 @@ The following shows the definition of the `@Auditable` annotation:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@@ -583,7 +583,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
@@ -597,7 +597,7 @@ The following shows the advice that matches the execution of `@Auditable` method
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Before("com.xyz.Pointcuts.publicMethod() && @annotation(auditable)") // <1>
public void audit(Auditable auditable) {
@@ -609,7 +609,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Before("com.xyz.Pointcuts.publicMethod() && @annotation(auditable)") // <1>
fun audit(auditable: Auditable) {
@@ -630,7 +630,7 @@ you have a generic type like the following:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
public interface Sample<T> {
void sampleGenericMethod(T param);
@@ -640,7 +640,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
interface Sample<T> {
fun sampleGenericMethod(param: T)
@@ -656,7 +656,7 @@ tying the advice parameter to the parameter type for which you want to intercept
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
public void beforeSampleMethod(MyType param) {
@@ -666,7 +666,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
fun beforeSampleMethod(param: MyType) {
@@ -682,7 +682,7 @@ pointcut as follows:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
public void beforeSampleMethod(Collection<MyType> param) {
@@ -692,7 +692,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
fun beforeSampleMethod(param: Collection<MyType>) {
@@ -756,7 +756,7 @@ The following example shows how to use the `argNames` attribute:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Before(
value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // <1>
@@ -771,7 +771,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Before(
value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // <1>
@@ -794,7 +794,7 @@ point object, the `argNames` attribute does not need to include it:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Before(
value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // <1>
@@ -809,7 +809,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Before(
value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // <1>
@@ -833,7 +833,7 @@ the `argNames` attribute:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Before("com.xyz.Pointcuts.publicMethod()") // <1>
public void audit(JoinPoint jp) {
@@ -844,7 +844,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Before("com.xyz.Pointcuts.publicMethod()") // <1>
fun audit(jp: JoinPoint) {
@@ -867,7 +867,7 @@ The following example shows how to do so:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Around("execution(List<Account> find*(..)) && " +
"com.xyz.CommonPointcuts.inDataAccessLayer() && " +
@@ -882,7 +882,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Around("execution(List<Account> find*(..)) && " +
"com.xyz.CommonPointcuts.inDataAccessLayer() && " +
@@ -923,7 +923,7 @@ Each of the distinct advice types of a particular aspect is conceptually meant t
to the join point directly. As a consequence, an `@AfterThrowing` advice method is not
supposed to receive an exception from an accompanying `@After`/`@AfterReturning` method.
Advice methods defined in the same `@Aspect` class that
As of Spring Framework 5.2.7, advice methods defined in the same `@Aspect` class that
need to run at the same join point are assigned precedence based on their advice type in
the following order, from highest to lowest precedence: `@Around`, `@Before`, `@After`,
`@AfterReturning`, `@AfterThrowing`. Note, however, that an `@After` advice method will
@@ -937,3 +937,5 @@ reflection for javac-compiled classes). Consider collapsing such advice methods
advice method per join point in each `@Aspect` class or refactor the pieces of advice into
separate `@Aspect` classes that you can order at the aspect level via `Ordered` or `@Order`.
====
@@ -8,8 +8,54 @@ 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 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).
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.
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
@@ -9,12 +9,43 @@ 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`:
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
[source,xml,indent=0,subs="verbatim"]
----
<bean id="myAspect" class="com.xyz.NotVeryUsefulAspect">
<!-- configure properties of the aspect here -->
</bean>
----
The second of the two examples shows the `NotVeryUsefulAspect` class definition, which is
annotated with `@Aspect`:
include-code::./NotVeryUsefulAspect[tag=snippet,indent=0]
[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
----
======
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)
@@ -32,3 +63,6 @@ stereotype annotation that qualifies, as per the rules of Spring's component sca
NOTE: In Spring AOP, aspects themselves cannot be the targets of advice from other
aspects. The `@Aspect` annotation on a class marks it as an aspect and, hence, excludes
it from auto-proxying.
@@ -16,10 +16,93 @@ 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:
include-code::./ConcurrentOperationExecutor[tag=snippet,indent=0]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Aspect
public class ConcurrentOperationExecutor implements Ordered {
`@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].
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].
======
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
@@ -31,15 +114,70 @@ we have exhausted all of our retry attempts.
The corresponding Spring configuration follows:
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
[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>
----
To refine the aspect so that it retries only idempotent operations, we might define the following
`Idempotent` annotation:
include-code::./service/Idempotent[tag=snippet,indent=0]
[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
----
======
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:
include-code::./service/SampleService[tag=snippet,indent=0]
[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? {
// ...
}
----
======
@@ -17,7 +17,7 @@ annotation. Consider the following example:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Aspect("perthis(execution(* com.xyz..service.*.*(..)))")
public class MyAspect {
@@ -33,7 +33,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Aspect("perthis(execution(* com.xyz..service.*.*(..)))")
class MyAspect {
@@ -60,3 +60,6 @@ Programming Guide for more information on `per` clauses.
The `pertarget` instantiation model works in exactly the same way as `perthis`, but it
creates one aspect instance for each unique target object at matched join points.
@@ -9,13 +9,13 @@ You can make an introduction by using the `@DeclareParents` annotation. This ann
is used to declare that matching types have a new parent (hence the name). For example,
given an interface named `UsageTracked` and an implementation of that interface named
`DefaultUsageTracked`, the following aspect declares that all implementors of service
interfaces also implement the `UsageTracked` interface (for example, for statistics via JMX):
interfaces also implement the `UsageTracked` interface (e.g. for statistics via JMX):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Aspect
public class UsageTracking {
@@ -33,7 +33,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Aspect
class UsageTracking {
@@ -63,15 +63,17 @@ you would write the following:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
UsageTracked usageTracked = context.getBean("myService", UsageTracked.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
val usageTracked = context.getBean<UsageTracked>("myService")
val usageTracked = context.getBean("myService", UsageTracked.class)
----
======
@@ -19,7 +19,7 @@ matches the execution of any method named `transfer`:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Pointcut("execution(* transfer(..))") // the pointcut expression
private void anyOldTransfer() {} // the pointcut signature
@@ -27,7 +27,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Pointcut("execution(* transfer(..))") // the pointcut expression
private fun anyOldTransfer() {} // the pointcut signature
@@ -104,7 +104,7 @@ Note that pointcut definitions are generally matched against any intercepted met
If a pointcut is strictly meant to be public-only, even in a CGLIB proxy scenario with
potential non-public interactions through proxies, it needs to be defined accordingly.
If your interception needs to include method calls or even constructors within the target
If your interception needs include method calls or even constructors within the target
class, consider the use of Spring-driven xref:core/aop/using-aspectj.adoc#aop-aj-ltw[native AspectJ weaving] instead
of Spring's proxy-based AOP framework. This constitutes a different mode of AOP usage
with different characteristics, so be sure to make yourself familiar with weaving
@@ -150,7 +150,7 @@ pointcut expressions by name. The following example shows three pointcut express
======
Java::
+
[source,java,indent=0,subs="verbatim",chomp="-packages"]
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz;
@@ -174,7 +174,7 @@ trading module.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz
@@ -217,7 +217,7 @@ expressions for this purpose. Such a class typically resembles the following
======
Java::
+
[source,java,indent=0,subs="verbatim",chomp="-packages",fold="none"]
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages",fold="none"]
----
package com.xyz;
@@ -279,7 +279,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",chomp="-packages",fold="none"]
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages",fold="none"]
----
package com.xyz
@@ -581,3 +581,6 @@ performance (time and memory used), due to extra processing and analysis. Scopin
designators are very fast to match, and using them means AspectJ can very quickly
dismiss groups of join points that should not be further processed. A good
pointcut should always include one if possible.
@@ -8,6 +8,7 @@ decisions are influenced by a number of factors including application requiremen
development tools, and team familiarity with AOP.
[[aop-spring-or-aspectj]]
== Spring AOP or Full AspectJ?
@@ -30,6 +31,7 @@ the @AspectJ style, sticking with regular Java compilation in your IDE, and addi
an aspect weaving phase to your build script.
[[aop-ataspectj-or-xml]]
== @AspectJ or XML for Spring AOP?
@@ -57,7 +59,7 @@ For example, in the @AspectJ style you can write something like the following:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Pointcut("execution(* get*())")
public void propertyAccess() {}
@@ -71,7 +73,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Pointcut("execution(* get*())")
fun propertyAccess() {}
@@ -105,3 +107,7 @@ Spring AOP and by AspectJ. So, if you later decide you need the capabilities of
to implement additional requirements, you can easily migrate to a classic AspectJ setup.
In general, the Spring team prefers the @AspectJ style for custom aspects beyond simple
configuration of enterprise services.
@@ -65,7 +65,7 @@ with less potential for errors. For example, you do not need to invoke the `proc
method on the `JoinPoint` used for around advice, and, hence, you cannot fail to invoke it.
All advice parameters are statically typed so that you work with advice parameters of
the appropriate type (for example, the type of the return value from a method execution) rather
the appropriate type (e.g. the type of the return value from a method execution) rather
than `Object` arrays.
The concept of join points matched by pointcuts is the key to AOP, which distinguishes
@@ -73,3 +73,7 @@ it from older technologies offering only interception. Pointcuts enable advice t
targeted independently of the object-oriented hierarchy. For example, you can apply an
around advice providing declarative transaction management to a set of methods that span
multiple objects (such as all business operations in the service layer).
@@ -14,5 +14,9 @@ need to advise a method that is not declared on an interface or where you need t
pass a proxied object to a method as a concrete type.
It is important to grasp the fact that Spring AOP is proxy-based. See
xref:core/aop/proxying.adoc#aop-understanding-aop-proxies[Understanding AOP Proxies]
for a thorough examination of exactly what this implementation detail actually means.
xref:core/aop/proxying.adoc#aop-understanding-aop-proxies[Understanding AOP Proxies] for a thorough examination of exactly what this
implementation detail actually means.
@@ -52,6 +52,10 @@ configuration-style approach. The fact that this chapter chooses to introduce th
@AspectJ-style approach first should not be taken as an indication that the Spring team
favors the @AspectJ annotation-style approach over the Spring XML configuration-style.
See xref:core/aop/choosing.adoc[Choosing which AOP Declaration Style to Use] for a more
complete discussion of the advantages and disadvantages of each style.
See xref:core/aop/choosing.adoc[Choosing which AOP Declaration Style to Use] for a more complete discussion of the advantages and disadvantages of
each style.
====
@@ -6,3 +6,7 @@ It is perfectly possible to mix @AspectJ style aspects by using the auto-proxyin
schema-defined `<aop:aspect>` aspects, `<aop:advisor>` declared advisors, and even proxies
and interceptors in other styles in the same configuration. All of these are implemented
by using the same underlying support mechanism and can co-exist without any difficulty.
@@ -6,26 +6,22 @@ target object. JDK dynamic proxies are built into the JDK, whereas CGLIB is a co
open-source class definition library (repackaged into `spring-core`).
If the target object to be proxied implements at least one interface, a JDK dynamic
proxy is used, and all of the interfaces implemented by the target type are proxied.
If the target object does not implement any interfaces, a CGLIB proxy is created which
is a runtime-generated subclass of the target type.
proxy is used. All of the interfaces implemented by the target type are proxied.
If the target object does not implement any interfaces, a CGLIB proxy is created.
If you want to force the use of CGLIB proxying (for example, to proxy every method
defined for the target object, not only those implemented by its interfaces),
you can do so. However, you should consider the following issues:
* `final` classes cannot be proxied, because they cannot be extended.
* `final` methods cannot be advised, because they cannot be overridden.
* `private` methods cannot be advised, because they cannot be overridden.
* Methods that are not visible for example, package-private methods in a parent class
from a different package cannot be advised because they are effectively private.
* The constructor of your proxied object will not be called twice, since the CGLIB proxy
instance is created through Objenesis. However, if your JVM does not allow for
constructor bypassing, you might see double invocations and corresponding debug log
entries from Spring's AOP support.
* Your CGLIB proxy usage may face limitations with the Java Module System. As a typical
case, you cannot create a CGLIB proxy for a class from the `java.lang` package when
deploying on the module path. Such cases require a JVM bootstrap flag
* With CGLIB, `final` methods cannot be advised, as they cannot be overridden in
runtime-generated subclasses.
* As of Spring 4.0, the constructor of your proxied object is NOT called twice anymore,
since the CGLIB proxy instance is created through Objenesis. Only if your JVM does
not allow for constructor bypassing, you might see double invocations and
corresponding debug log entries from Spring's AOP support.
* Your CGLIB proxy usage may face limitations with the JDK 9+ platform module system.
As a typical case, you cannot create a CGLIB proxy for a class from the `java.lang`
package when deploying on the module path. Such cases require a JVM bootstrap flag
`--add-opens=java.base/java.lang=ALL-UNNAMED` which is not available for modules.
To force the use of CGLIB proxies, set the value of the `proxy-target-class` attribute
@@ -61,6 +57,7 @@ proxies _for all three of them_.
====
[[aop-understanding-aop-proxies]]
== Understanding AOP Proxies
@@ -68,14 +65,15 @@ Spring AOP is proxy-based. It is vitally important that you grasp the semantics
what that last statement actually means before you write your own aspects or use any of
the Spring AOP-based aspects supplied with the Spring Framework.
Consider first the scenario where you have a plain-vanilla, un-proxied object reference,
as the following code snippet shows:
Consider first the scenario where you have a plain-vanilla, un-proxied,
nothing-special-about-it, straight object reference, as the following
code snippet shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
public class SimplePojo implements Pojo {
@@ -92,7 +90,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
class SimplePojo : Pojo {
@@ -117,7 +115,7 @@ image::aop-proxy-plain-pojo-call.png[]
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
public class Main {
@@ -131,7 +129,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun main() {
val pojo = SimplePojo()
@@ -150,7 +148,7 @@ image::aop-proxy-call.png[]
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
public class Main {
@@ -168,7 +166,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun main() {
val factory = ProxyFactory(SimplePojo())
@@ -189,35 +187,26 @@ the interceptors (advice) that are relevant to that particular method call. Howe
once the call has finally reached the target object (the `SimplePojo` reference in
this case), any method calls that it may make on itself, such as `this.bar()` or
`this.foo()`, are going to be invoked against the `this` reference, and not the proxy.
This has important implications. It means that self invocation is not going to result
in the advice associated with a method invocation getting a chance to run. In other words,
self invocation via an explicit or implicit `this` reference will bypass the advice.
This has important implications. It means that self-invocation is not going to result
in the advice associated with a method invocation getting a chance to run.
To address that, you have the following options.
Avoid self invocation ::
The best approach (the term "best" is used loosely here) is to refactor your code such
that the self invocation does not happen. This does entail some work on your part, but
it is the best, least-invasive approach.
Inject a self reference ::
An alternative approach is to make use of
xref:core/beans/annotation-config/autowired.adoc#beans-autowired-annotation-self-injection[self injection],
and invoke methods on the proxy via the self reference instead of via `this`.
Use `AopContext.currentProxy()` ::
This last approach is highly discouraged, and we hesitate to point it out, in favor of
the previous options. However, as a last resort you can choose to tie the logic within
your class to Spring AOP, as the following example shows.
Okay, so what is to be done about this? The best approach (the term "best" is used
loosely here) is to refactor your code such that the self-invocation does not happen.
This does entail some work on your part, but it is the best, least-invasive approach.
The next approach is absolutely horrendous, and we hesitate to point it out, precisely
because it is so horrendous. You can (painful as it is to us) totally tie the logic
within your class to Spring AOP, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
public class SimplePojo implements Pojo {
public void foo() {
// This works, but it should be avoided if possible.
// this works, but... gah!
((Pojo) AopContext.currentProxy()).bar();
}
@@ -229,12 +218,12 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
class SimplePojo : Pojo {
fun foo() {
// This works, but it should be avoided if possible.
// this works, but... gah!
(AopContext.currentProxy() as Pojo).bar()
}
@@ -245,16 +234,16 @@ Kotlin::
----
======
The use of `AopContext.currentProxy()` totally couples your code to Spring AOP, and it
makes the class itself aware of the fact that it is being used in an AOP context, which
reduces some of the benefits of AOP. It also requires that the `ProxyFactory` is
configured to expose the proxy, as the following example shows:
This totally couples your code to Spring AOP, and it makes the class itself aware of
the fact that it is being used in an AOP context, which flies in the face of AOP. It
also requires some additional configuration when the proxy is being created, as the
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
public class Main {
@@ -273,7 +262,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun main() {
val factory = ProxyFactory(SimplePojo())
@@ -288,5 +277,9 @@ Kotlin::
----
======
NOTE: AspectJ compile-time weaving and load-time weaving do not have this self-invocation
issue because they apply advice within the bytecode instead of via a proxy.
Finally, it must be noted that AspectJ does not have this self-invocation issue because
it is not a proxy-based AOP framework.
@@ -26,6 +26,7 @@ use either only the `<aop:config>` style or only the `AutoProxyCreator` style an
never mix them.
[[aop-schema-declaring-an-aspect]]
== Declaring an Aspect
@@ -53,6 +54,7 @@ The bean that backs the aspect (`aBean` in this case) can of course be configure
dependency injected just like any other Spring bean.
[[aop-schema-pointcuts]]
== Declaring a Pointcut
@@ -134,7 +136,7 @@ parameters of the matching names, as follows:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
public void monitor(Object service) {
// ...
@@ -143,7 +145,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun monitor(service: Any) {
// ...
@@ -175,7 +177,9 @@ follows:
Note that pointcuts defined in this way are referred to by their XML `id` and cannot be
used as named pointcuts to form composite pointcuts. The named pointcut support in the
schema-based definition style is thus more limited than that offered by the @AspectJ style.
schema-based definition style is thus more limited than that offered by the @AspectJ
style.
[[aop-schema-advice]]
@@ -184,6 +188,7 @@ schema-based definition style is thus more limited than that offered by the @Asp
The schema-based AOP support uses the same five kinds of advice as the @AspectJ style, and they have
exactly the same semantics.
[[aop-schema-advice-before]]
=== Before Advice
@@ -232,6 +237,7 @@ that contains the advice. Before a data access operation is performed (a method
join point matched by the pointcut expression), the `doAccessCheck` method on the aspect
bean is invoked.
[[aop-schema-advice-after-returning]]
=== After Returning Advice
@@ -276,19 +282,20 @@ example, you can declare the method signature as follows:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
public void doAccessCheck(Object retVal) {...
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun doAccessCheck(retVal: Any) {...
----
======
[[aop-schema-advice-after-throwing]]
=== After Throwing Advice
@@ -333,19 +340,20 @@ The type of this parameter constrains matching in the same way as described for
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
public void doRecoveryActions(DataAccessException dataAccessEx) {...
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun doRecoveryActions(dataAccessEx: DataAccessException) {...
----
======
[[aop-schema-advice-after-finally]]
=== After (Finally) Advice
@@ -364,6 +372,7 @@ You can declare it by using the `after` element, as the following example shows:
</aop:aspect>
----
[[aop-schema-advice-around]]
=== Around Advice
@@ -412,7 +421,7 @@ The implementation of the `doBasicProfiling` advice can be exactly the same as i
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
@@ -424,7 +433,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any? {
// start stopwatch
@@ -435,18 +444,17 @@ Kotlin::
----
======
[[aop-schema-params]]
=== Advice Parameters
The schema-based declaration style supports fully typed advice in the same way as
described for the @AspectJ support -- by matching pointcut parameters by name against
advice method parameters. See
xref:core/aop/ataspectj/advice.adoc#aop-ataspectj-advice-params[Advice Parameters] for details.
If you wish to explicitly specify argument names for the advice methods (not relying on the
advice method parameters. See xref:core/aop/ataspectj/advice.adoc#aop-ataspectj-advice-params[Advice Parameters] for details. If you wish
to explicitly specify argument names for the advice methods (not relying on the
detection strategies previously described), you can do so by using the `arg-names`
attribute of the advice element, which is treated in the same manner as the `argNames`
attribute in an advice annotation (as described in
xref:core/aop/ataspectj/advice.adoc#aop-ataspectj-advice-params-names[Determining Argument Names]).
attribute in an advice annotation (as described in xref:core/aop/ataspectj/advice.adoc#aop-ataspectj-advice-params-names[Determining Argument Names]).
The following example shows how to specify an argument name in XML:
[source,xml,indent=0,subs="verbatim"]
@@ -456,8 +464,7 @@ The following example shows how to specify an argument name in XML:
method="audit"
arg-names="auditable" />
----
<1> References the `publicMethod` named pointcut defined in
xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
The `arg-names` attribute accepts a comma-delimited list of parameter names.
@@ -468,7 +475,7 @@ some around advice used in conjunction with a number of strongly typed parameter
======
Java::
+
[source,java,indent=0,subs="verbatim",chomp="-packages"]
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz.service;
@@ -487,7 +494,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz.service
@@ -514,7 +521,7 @@ proceed with the method call. The presence of this parameter is an indication th
======
Java::
+
[source,java,indent=0,subs="verbatim",chomp="-packages"]
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz;
@@ -538,7 +545,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz
@@ -603,7 +610,7 @@ Consider the following driver script:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
public class Boot {
@@ -617,7 +624,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun main() {
val ctx = ClassPathXmlApplicationContext("beans.xml")
@@ -638,15 +645,15 @@ ms % Task name
00000 ? execution(getFoo)
----
[[aop-ordering]]
=== Advice Ordering
When multiple pieces of advice need to run at the same join point (executing method)
the ordering rules are as described in
xref:core/aop/ataspectj/advice.adoc#aop-ataspectj-advice-ordering[Advice Ordering]. The
precedence between aspects is determined via the `order` attribute in the `<aop:aspect>`
element or by either adding the `@Order` annotation to the bean that backs the aspect
or by having the bean implement the `Ordered` interface.
the ordering rules are as described in xref:core/aop/ataspectj/advice.adoc#aop-ataspectj-advice-ordering[Advice Ordering]. The precedence
between aspects is determined via the `order` attribute in the `<aop:aspect>` element or
by either adding the `@Order` annotation to the bean that backs the aspect or by having
the bean implement the `Ordered` interface.
[NOTE]
====
@@ -669,6 +676,7 @@ at the aspect level.
====
[[aop-schema-introductions]]
== Introductions
@@ -706,7 +714,7 @@ The class that backs the `usageTracking` bean would then contain the following m
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
public void recordUsage(UsageTracked usageTracked) {
usageTracked.incrementUseCount();
@@ -715,7 +723,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun recordUsage(usageTracked: UsageTracked) {
usageTracked.incrementUseCount()
@@ -734,20 +742,21 @@ following:
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
UsageTracked usageTracked = context.getBean("myService", UsageTracked.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
val usageTracked = context.getBean("myService", UsageTracked.class)
----
======
[[aop-schema-instantiation-models]]
== Aspect Instantiation Models
@@ -755,6 +764,7 @@ The only supported instantiation model for schema-defined aspects is the singlet
model. Other instantiation models may be supported in future releases.
[[aop-schema-advisors]]
== Advisors
@@ -762,8 +772,7 @@ The concept of "advisors" comes from the AOP support defined in Spring
and does not have a direct equivalent in AspectJ. An advisor is like a small
self-contained aspect that has a single piece of advice. The advice itself is
represented by a bean and must implement one of the advice interfaces described in
xref:core/aop-api/advice.adoc#aop-api-advice-types[Advice Types in Spring].
Advisors can take advantage of AspectJ pointcut expressions.
xref:core/aop-api/advice.adoc#aop-api-advice-types[Advice Types in Spring]. Advisors can take advantage of AspectJ pointcut expressions.
Spring supports the advisor concept with the `<aop:advisor>` element. You most
commonly see it used in conjunction with transactional advice, which also has its own
@@ -796,6 +805,7 @@ To define the precedence of an advisor so that the advice can participate in ord
use the `order` attribute to define the `Ordered` value of the advisor.
[[aop-schema-example]]
== An AOP Schema Example
@@ -819,7 +829,7 @@ call `proceed` multiple times. The following listing shows the basic aspect impl
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
public class ConcurrentOperationExecutor implements Ordered {
@@ -859,7 +869,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
class ConcurrentOperationExecutor : Ordered {
@@ -943,7 +953,7 @@ to annotate the implementation of service operations, as the following example s
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Retention(RetentionPolicy.RUNTIME)
// marker annotation
@@ -953,7 +963,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Retention(AnnotationRetention.RUNTIME)
// marker annotation
@@ -971,3 +981,7 @@ pointcut expression so that only `@Idempotent` operations match, as follows:
expression="execution(* com.xyz.service.*.*(..)) and
@annotation(com.xyz.service.Idempotent)"/>
----
@@ -8,14 +8,12 @@ alone.
Spring ships with a small AspectJ aspect library, which is available stand-alone in your
distribution as `spring-aspects.jar`. You need to add this to your classpath in order
to use the aspects in it.
xref:core/aop/using-aspectj.adoc#aop-atconfigurable[Using AspectJ to Dependency Inject Domain Objects with Spring]
and xref:core/aop/using-aspectj.adoc#aop-ajlib-other[Other Spring aspects for AspectJ]
discuss the content of this library and how you can use it.
xref:core/aop/using-aspectj.adoc#aop-aj-configure[Configuring AspectJ Aspects by Using Spring IoC]
discusses how to dependency inject AspectJ aspects that are woven using the AspectJ compiler. Finally,
xref:core/aop/using-aspectj.adoc#aop-aj-ltw[Load-time Weaving with AspectJ in the Spring Framework]
provides an introduction to load-time weaving for Spring applications that use AspectJ.
to use the aspects in it. xref:core/aop/using-aspectj.adoc#aop-atconfigurable[Using AspectJ to Dependency Inject Domain Objects with Spring] and xref:core/aop/using-aspectj.adoc#aop-ajlib-other[Other Spring aspects for AspectJ] discuss the
content of this library and how you can use it. xref:core/aop/using-aspectj.adoc#aop-aj-configure[Configuring AspectJ Aspects by Using Spring IoC] discusses how to
dependency inject AspectJ aspects that are woven using the AspectJ compiler. Finally,
xref:core/aop/using-aspectj.adoc#aop-aj-ltw[Load-time Weaving with AspectJ in the Spring Framework] provides an introduction to load-time weaving for Spring applications
that use AspectJ.
[[aop-atconfigurable]]
@@ -38,7 +36,7 @@ following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim",chomp="-packages"]
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz.domain;
@@ -52,7 +50,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz.domain
@@ -68,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 defined via XML is the
(`com.xyz.domain.Account`). Since the default name for a bean 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:
@@ -86,7 +84,7 @@ can do so directly in the annotation, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim",chomp="-packages"]
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz.domain;
@@ -100,7 +98,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz.domain
@@ -155,14 +153,14 @@ available for use in the body of the constructors, you need to define this on th
======
Java::
+
[source,java,indent=0,subs="verbatim"]
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configurable(preConstruction = true)
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim"]
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configurable(preConstruction = true)
----
@@ -179,10 +177,41 @@ 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). You can define
the related configuration as follows:
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:
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
[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/>
----
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
@@ -208,6 +237,7 @@ not use `@Configurable` on bean classes that are registered as regular Spring be
with the container. Doing so results in double initialization, once through the
container and once through the aspect.
[[aop-configurable-testing]]
=== Unit Testing `@Configurable` Objects
@@ -220,6 +250,7 @@ you can still unit test outside of the container as normal, but you see a warnin
message each time that you construct a `@Configurable` object indicating that it has
not been configured by Spring.
[[aop-configurable-container]]
=== Working with Multiple Application Contexts
@@ -249,6 +280,7 @@ is added only to the container-wide classpath (and hence loaded by the shared pa
not what you want).
[[aop-ajlib-other]]
== Other Spring aspects for AspectJ
@@ -301,6 +333,7 @@ fully qualified class names:
----
[[aop-aj-configure]]
== Configuring AspectJ Aspects by Using Spring IoC
@@ -355,6 +388,7 @@ results in the creation of Spring AOP proxies. The @AspectJ style of aspect
declaration is being used here, but the AspectJ runtime is not involved.
[[aop-aj-ltw]]
== Load-time Weaving with AspectJ in the Spring Framework
@@ -388,6 +422,7 @@ LTW that uses Spring, followed by detailed specifics about elements introduced i
example. For a complete example, see the
{petclinic-github-org}/spring-framework-petclinic[Petclinic sample application based on Spring Framework].
[[aop-aj-ltw-first-example]]
=== A First Example
@@ -409,7 +444,7 @@ It is a time-based profiler that uses the @AspectJ-style of aspect declaration:
======
Java::
+
[source,java,indent=0,subs="verbatim",chomp="-packages"]
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz;
@@ -442,7 +477,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz
@@ -540,7 +575,7 @@ driver class with a `main(..)` method to demonstrate the LTW in action:
======
Java::
+
[source,java,indent=0,subs="verbatim",chomp="-packages"]
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz;
@@ -562,7 +597,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz
@@ -621,7 +656,7 @@ result:
======
Java::
+
[source,java,indent=0,subs="verbatim",chomp="-packages"]
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz;
@@ -643,7 +678,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz
@@ -673,6 +708,7 @@ nice example of a development-time aspect that developers can use during develop
and then easily exclude from builds of the application being deployed
into UAT or production.
[[aop-aj-ltw-the-aspects]]
=== Aspects
@@ -681,6 +717,7 @@ either the AspectJ language itself, or you can write your aspects in the @Aspect
Your aspects are then both valid AspectJ and Spring AOP aspects.
Furthermore, the compiled aspect classes need to be available on the classpath.
[[aop-aj-ltw-aop_dot_xml]]
=== `META-INF/aop.xml`
@@ -710,6 +747,7 @@ The structure and contents of this file is detailed in the LTW part of the
{aspectj-docs-devguide}/ltw-configuration.html[AspectJ reference
documentation]. Because the `aop.xml` file is 100% AspectJ, we do not describe it further here.
[[aop-aj-ltw-libraries]]
=== Required libraries (JARS)
@@ -724,6 +762,7 @@ If you use the xref:core/aop/using-aspectj.adoc#aop-aj-ltw-environments-generic[
* `spring-instrument.jar`
[[aop-aj-ltw-spring]]
=== Spring Configuration
@@ -744,9 +783,52 @@ 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` as follows:
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:
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
[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>
----
The preceding configuration automatically defines and registers a number of LTW-specific
infrastructure beans, such as a `LoadTimeWeaver` and an `AspectJWeavingEnabler`, for you.
@@ -782,12 +864,63 @@ 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 configure a specific `LoadTimeWeaver`, implement the
`LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method
(or use the XML equivalent).
To specify a specific `LoadTimeWeaver` with Java configuration, implement the
`LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method.
The following example specifies a `ReflectiveLoadTimeWeaver`:
include-code::./CustomWeaverConfiguration[tag=snippet,indent=0]
[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>
----
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`.
@@ -823,6 +956,7 @@ possible values:
then AspectJ weaving is on. Otherwise, it is off. This is the default value.
|===
[[aop-aj-ltw-environments]]
=== Environment-specific Configuration
@@ -871,3 +1005,7 @@ Note that this requires modification of the JVM launch script, which may prevent
from using this in application server environments (depending on your server and your
operation policies). That said, for one-app-per-JVM deployments such as standalone
Spring Boot applications, you typically control the entire JVM setup in any case.
+61 -226
View File
@@ -5,7 +5,6 @@ This chapter covers Spring's Ahead of Time (AOT) optimizations.
For AOT support specific to integration tests, see xref:testing/testcontext-framework/aot.adoc[Ahead of Time Support for Tests].
[[aot.introduction]]
== Introduction to Ahead of Time Optimizations
@@ -18,9 +17,9 @@ Applying such optimizations early implies the following restrictions:
* The beans defined in your application cannot change at runtime, meaning:
** `@Profile`, in particular profile-specific configuration, needs to be chosen at build time and is automatically enabled at runtime when AOT is enabled.
** `Environment` properties that impact the presence of a bean (`@Conditional`) are only considered at build time.
* Bean definitions with instance suppliers (lambdas or method references) cannot be transformed ahead of time.
* Bean definitions with instance suppliers (lambdas or method references) cannot be transformed ahead-of-time.
* Beans registered as singletons (using `registerSingleton`, typically from
`ConfigurableListableBeanFactory`) cannot be transformed ahead of time either.
`ConfigurableListableBeanFactory`) cannot be transformed ahead-of-time either.
* As we cannot rely on the instance, make sure that the bean type is as precise as
possible.
@@ -36,7 +35,6 @@ A Spring AOT processed application typically generates:
NOTE: At the moment, AOT is focused on allowing Spring applications to be deployed as native images using GraalVM.
We intend to support more JVM-based use cases in future generations.
[[aot.basics]]
== AOT Engine Overview
@@ -53,7 +51,6 @@ The `RuntimeHints` instance can also be used to generate the relevant GraalVM na
Those steps are covered in greater detail in the sections below.
[[aot.refresh]]
== Refresh for AOT Processing
@@ -91,7 +88,6 @@ This makes sure to create any proxy that will be required at runtime.
Once this part completes, the `BeanFactory` contains the bean definitions that are necessary for the application to run. It does not trigger bean instantiation but allows the AOT engine to inspect the beans that will be created at runtime.
[[aot.bean-factory-initialization-contributions]]
== Bean Factory Initialization AOT Contributions
@@ -110,10 +106,11 @@ Consequently, such a bean is automatically excluded from the AOT-optimized conte
[NOTE]
====
If a bean implements the `BeanFactoryInitializationAotProcessor` interface, the bean and **all** of its dependencies will be initialized during AOT processing.
We generally recommend that this interface is only implemented by infrastructure beans, such as a `BeanFactoryPostProcessor`, which have limited dependencies and are already initialized early in the bean factory lifecycle.
We generally recommend that this interface is only implemented by infrastructure beans such as `BeanFactoryPostProcessor` which have limited dependencies and are already initialized early in the bean factory lifecycle.
If such a bean is registered using an `@Bean` factory method, ensure the method is `static` so that its enclosing `@Configuration` class does not have to be initialized.
====
[[aot.bean-registration-contributions]]
=== Bean Registration AOT Contributions
@@ -130,7 +127,7 @@ Typically used when the bean definition needs to be tuned for specific features
[NOTE]
====
If a bean implements the `BeanRegistrationAotProcessor` interface, the bean and **all** of its dependencies will be initialized during AOT processing.
We generally recommend that this interface is only implemented by infrastructure beans, such as a `BeanFactoryPostProcessor`, which have limited dependencies and are already initialized early in the bean factory lifecycle.
We generally recommend that this interface is only implemented by infrastructure beans such as `BeanFactoryPostProcessor` which have limited dependencies and are already initialized early in the bean factory lifecycle.
If such a bean is registered using an `@Bean` factory method, ensure the method is `static` so that its enclosing `@Configuration` class does not have to be initialized.
====
@@ -143,7 +140,7 @@ Taking our previous example, let's assume that `DataSourceConfiguration` is as f
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration(proxyBeanMethods = false)
public class DataSourceConfiguration {
@@ -158,7 +155,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration(proxyBeanMethods = false)
class DataSourceConfiguration {
@@ -179,7 +176,7 @@ The AOT engine will convert the configuration class above to code similar to the
======
Java::
+
[source,java,indent=0]
[source,java,indent=0,role="primary"]
----
/**
* Bean definitions for {@link DataSourceConfiguration}
@@ -222,23 +219,21 @@ NOTE: The exact code generated may differ depending on the exact nature of your
TIP: Each generated class is annotated with `org.springframework.aot.generate.Generated` to
identify them if they need to be excluded, for instance by static analysis tools.
The generated code above creates bean definitions equivalent to the `@Configuration` class, but in a direct way and without the use of reflection at all if possible.
The generated code above creates bean definitions equivalent to the `@Configuration` class, but in a direct way and without the use of reflection if at all possible.
There is a bean definition for `dataSourceConfiguration` and one for `dataSourceBean`.
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
AOT is a mandatory step to transform a Spring application to a native executable, so it
is automatically enabled when running within a native image. However it is also possible to use AOT optimizations
is automatically enabled when running in this mode. It is possible to use those optimizations
on the JVM by setting the `spring.aot.enabled` System property to `true`.
NOTE: When AOT optimizations are included, some decisions that have been made at build time
are hard coded in the application setup. For instance, profiles that have been enabled at
build time are automatically enabled at runtime as well.
NOTE: When AOT optimizations are included, some decisions that have been taken at build-time
are hard-coded in the application setup. For instance, profiles that have been enabled at
build-time are automatically enabled at runtime as well.
[[aot.bestpractices]]
== Best Practices
@@ -276,14 +271,14 @@ build time.
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.
For `@Configuration` classes, make sure that the return type of an `@Bean` factory method is as precise as possible.
For `@Configuration` classes, make sure that the return type of the factory `@Bean` method is as precise as possible.
Consider the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration(proxyBeanMethods = false)
public class UserConfiguration {
@@ -295,32 +290,19 @@ Java::
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Configuration(proxyBeanMethods = false)
class UserConfiguration {
@Bean
fun myInterface(): MyInterface = MyImplementation()
}
----
======
In the example above, the declared type for the `myInterface` bean is `MyInterface`.
During AOT processing, none of the usual post-processing will take `MyImplementation` into account.
For instance, if there is an annotated handler method on `MyImplementation` that the context should register, it will not be detected during AOT processing.
In the example above, the declared type for the `myInterface` bean is `MyInterface`.
None of the usual post-processing will take `MyImplementation` into account.
For instance, if there is an annotated handler method on `MyImplementation` that the context should register, it wont be detected upfront.
The example above should therefore be rewritten as follows:
The example above should be rewritten as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration(proxyBeanMethods = false)
public class UserConfiguration {
@@ -332,19 +314,6 @@ Java::
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Configuration(proxyBeanMethods = false)
class UserConfiguration {
@Bean
fun myInterface() = MyImplementation()
}
----
======
If you are registering bean definitions programmatically, consider using `RootBeanBefinition` as it allows to specify a `ResolvableType` that handles generics.
@@ -353,7 +322,7 @@ If you are registering bean definitions programmatically, consider using `RootBe
=== Avoid Multiple Constructors
The container is able to choose the most appropriate constructor to use based on several candidates.
However, relying on that is not a best practice, and flagging the preferred constructor with `@Autowired` if necessary is preferred.
However, this is not a best practice and flagging the preferred constructor with `@Autowired` if necessary is preferred.
In case you are working on a code base that you 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.
@@ -368,13 +337,13 @@ A good rule of thumb is to keep in mind that bean definitions are an abstraction
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 `org.springframework.aot.generate.ValueCodeGenerator$Delegate` as the key.
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
Spring AOT detects what needs to be done to create a bean and translates that into generated code that uses 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] which can lead to several issues with AOT:
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:
. The custom arguments require dynamic introspection of a matching constructor or factory method.
Those arguments cannot be detected by AOT, so the necessary reflection hints will have to be provided manually.
@@ -401,7 +370,7 @@ for further information.
=== FactoryBean
`FactoryBean` should be used with care as it introduces an intermediate layer in terms of bean type resolution that may not be conceptually necessary.
As a rule of thumb, if a `FactoryBean` instance does not hold long-term state and is not needed at a later point at runtime, it should be replaced by a regular `@Bean` factory method, possibly with a `FactoryBean` adapter layer on top (for declarative configuration purposes).
As a rule of thumb, if the `FactoryBean` instance does not hold long-term state and is not needed at a later point in time at runtime, it should be replaced by a regular factory method, possibly with a `FactoryBean` adapter layer on top (for declarative configuration purposes).
If your `FactoryBean` implementation does not resolve the object type (i.e. `T`), extra care is necessary.
Consider the following example:
@@ -410,21 +379,12 @@ Consider the following example:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class ClientFactoryBean<T extends AbstractClient> implements FactoryBean<T> {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
class ClientFactoryBean<T : AbstractClient> : FactoryBean<T> {
// ...
}
----
======
A concrete client declaration should provide a resolved generic for the client, as shown in the following example:
@@ -433,7 +393,7 @@ A concrete client declaration should provide a resolved generic for the client,
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration(proxyBeanMethods = false)
public class UserConfiguration {
@@ -445,22 +405,9 @@ Java::
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Configuration(proxyBeanMethods = false)
class UserConfiguration {
@Bean
fun myClient() = ClientFactoryBean<MyClient>(...)
}
----
======
If a `FactoryBean` bean definition is registered programmatically, make sure to follow these steps:
If the `FactoryBean` bean definition is registered programmatically, make sure to follow these steps:
1. Use `RootBeanDefinition`.
2. Set the `beanClass` to the `FactoryBean` class so that AOT knows that it is an intermediate layer.
@@ -472,22 +419,12 @@ The following example showcases a basic definition:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
RootBeanDefinition beanDefinition = new RootBeanDefinition(ClientFactoryBean.class);
beanDefinition.setTargetType(ResolvableType.forClassWithGenerics(ClientFactoryBean.class, MyClient.class));
// ...
registry.registerBeanDefinition("myClient", beanDefinition);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
val beanDefinition = RootBeanDefinition(ClientFactoryBean::class.java)
beanDefinition.setTargetType(ResolvableType.forClassWithGenerics(ClientFactoryBean::class.java, MyClient::class.java));
// ...
registry.registerBeanDefinition("myClient", beanDefinition)
RootBeanDefinition beanDefinition = new RootBeanDefinition(ClientFactoryBean.class);
beanDefinition.setTargetType(ResolvableType.forClassWithGenerics(ClientFactoryBean.class, MyClient.class));
// ...
registry.registerBeanDefinition("myClient", beanDefinition);
----
======
@@ -500,7 +437,7 @@ The JPA persistence unit has to be known upfront for certain optimizations to ap
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Bean
LocalContainerEntityManagerFactoryBean customDBEntityManagerFactory(DataSource dataSource) {
@@ -510,29 +447,16 @@ Java::
return factoryBean;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Bean
fun customDBEntityManagerFactory(dataSource: DataSource): LocalContainerEntityManagerFactoryBean {
val factoryBean = LocalContainerEntityManagerFactoryBean()
factoryBean.dataSource = dataSource
factoryBean.setPackagesToScan("com.example.app")
return factoryBean
}
----
======
To ensure that entity scanning occurs ahead of time, a `PersistenceManagedTypes` bean must be declared and used by the
To make sure the scanning occurs ahead of time, a `PersistenceManagedTypes` bean must be declared and used by the
factory bean definition, as shown by the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Bean
PersistenceManagedTypes persistenceManagedTypes(ResourceLoader resourceLoader) {
@@ -548,28 +472,8 @@ Java::
return factoryBean;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@Bean
fun persistenceManagedTypes(resourceLoader: ResourceLoader): PersistenceManagedTypes {
return PersistenceManagedTypesScanner(resourceLoader)
.scan("com.example.app")
}
@Bean
fun customDBEntityManagerFactory(dataSource: DataSource, managedTypes: PersistenceManagedTypes): LocalContainerEntityManagerFactoryBean {
val factoryBean = LocalContainerEntityManagerFactoryBean()
factoryBean.dataSource = dataSource
factoryBean.setManagedTypes(managedTypes)
return factoryBean
}
----
======
[[aot.hints]]
== Runtime Hints
@@ -585,17 +489,10 @@ The following example makes sure that `config/app.properties` can be loaded from
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
runtimeHints.resources().registerPattern("config/app.properties");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
runtimeHints.resources().registerPattern("config/app.properties")
----
======
A number of contracts are handled automatically during AOT processing.
@@ -604,130 +501,68 @@ For instance, the return type of a `@Controller` method is inspected, and releva
For cases that the core container cannot infer, you can register such hints programmatically.
A number of convenient annotations are also provided for common use cases.
[[aot.hints.import-runtime-hints]]
=== `@ImportRuntimeHints`
{spring-framework-api}/aot/hint/RuntimeHintsRegistrar.html[`RuntimeHintsRegistrar`]
implementations allow you to get a callback to the `RuntimeHints` instance managed by the
AOT engine. Implementations of this interface can be registered using
{spring-framework-api}/context/annotation/ImportRuntimeHints.html[`@ImportRuntimeHints`]
on any Spring bean or `@Bean` factory method. `RuntimeHintsRegistrar` implementations are
detected and invoked at build time.
`RuntimeHintsRegistrar` implementations allow you to get a callback to the `RuntimeHints` instance managed by the AOT engine.
Implementations of this interface can be registered using `@ImportRuntimeHints` on any Spring bean or `@Bean` factory method.
`RuntimeHintsRegistrar` implementations are detected and invoked at build time.
include-code::./SpellCheckService[]
If at all possible, `@ImportRuntimeHints` should be used as close as possible to the component that requires the hints.
This way, if the component is not contributed to the `BeanFactory`, the hints will not be contributed either.
This way, if the component is not contributed to the `BeanFactory`, the hints won't be contributed either.
It is also possible to register an implementation statically by adding an entry in `META-INF/spring/aot.factories` with a key equal to the fully-qualified name of the `RuntimeHintsRegistrar` interface.
[[aot.hints.reflective]]
=== `@Reflective`
{spring-framework-api}/aot/hint/annotation/Reflective.html[`@Reflective`] provides an idiomatic way to flag the need for reflection on an annotated element.
For instance, `@EventListener` is meta-annotated with `@Reflective` since the underlying implementation invokes the annotated method using reflection.
Out-of-the-box, only Spring beans are considered, but you can opt-in for scanning using
{spring-framework-api}/context/annotation/ReflectiveScan.html[`@ReflectiveScan`]. In the
example below, all types in the `com.example.app` package and its subpackages are
considered:
include-code::./MyConfiguration[]
Scanning happens during AOT processing, and the types in the target packages do not need to have a class-level annotation to be considered.
This performs a _deep scan_, and the presence of `@Reflective`, either directly or as a meta-annotation, is checked on types, fields, constructors, methods, and enclosed elements.
By default, `@Reflective` registers an invocation hint for the annotated element.
This can be tuned by specifying a custom `ReflectiveProcessor` implementation via the `@Reflective` annotation.
By default, only Spring beans are considered, and an invocation hint is registered for the annotated element.
This can be tuned by specifying a custom `ReflectiveProcessor` implementation via the
`@Reflective` annotation.
Library authors can reuse this annotation for their own purposes.
An example of such customization is covered in the next section.
If components other than Spring beans need to be processed, a `BeanFactoryInitializationAotProcessor` can detect the relevant types and use `ReflectiveRuntimeHintsRegistrar` to process them.
[[aot.hints.register-reflection]]
=== `@RegisterReflection`
{spring-framework-api}/aot/hint/annotation/RegisterReflection.html[`@RegisterReflection`] is a specialization of `@Reflective` that provides a declarative way to register reflection for arbitrary types.
[[aot.hints.register-reflection-for-binding]]
=== `@RegisterReflectionForBinding`
NOTE: As a specialization of `@Reflective`, `@RegisterReflection` is also detected if you are using `@ReflectiveScan`.
In the following example, public constructors and public methods can be invoked via reflection on `AccountService`:
include-code::./MyConfiguration[tag=snippet,indent=0]
`@RegisterReflection` can be applied to any target type at the class level, but it can also be applied directly to a method to better indicate where the hints are actually required.
`@RegisterReflection` can be used as a meta-annotation to support more specific needs.
{spring-framework-api}/aot/hint/annotation/RegisterReflectionForBinding.html[`@RegisterReflectionForBinding`] is a composed annotation that is meta-annotated with `@RegisterReflection` and registers the need for serializing arbitrary types.
{spring-framework-api}/aot/hint/annotation/RegisterReflectionForBinding.html[`@RegisterReflectionForBinding`] is a specialization of `@Reflective` that registers the need for serializing arbitrary types.
A typical use case is the use of DTOs that the container cannot infer, such as using a web client within a method body.
The following example registers `Order` for serialization.
include-code::./OrderService[tag=snippet,indent=0]
This registers hints for constructors, fields, properties, and record components of `Order`.
Hints are also registered for types transitively used on properties and record components.
In other words, if `Order` exposes others types, hints are registered for those as well.
[[aot.hints.convention-based-conversion]]
=== Runtime Hints for Convention-based Conversion
Although the core container provides built-in support for automatic conversion of many
common types (see xref:core/validation/convert.adoc[Spring Type Conversion]), some
conversions are supported via a convention-based algorithm that relies on reflection.
Specifically, if there is no explicit `Converter` registered with the `ConversionService`
for a particular source &#8594; target type pair, the internal `ObjectToObjectConverter`
will attempt to use conventions to convert a source object to a target type by delegating
to a method on the source object or to a static factory method or constructor on the
target type. Since this convention-based algorithm can be applied to arbitrary types at
runtime, the core container is not able to infer the runtime hints necessary to support
such reflection.
If you encounter convention-based conversion issues within a native image resulting from
lacking runtime hints, you can register the necessary hints programmatically. For
example, if your application requires a conversion from `java.time.Instant` to
`java.sql.Timestamp` and relies on `ObjectToObjectConverter` to invoke
`java.sql.Timestamp.from(Instant)` using reflection, you could implement a custom
`RuntimeHintsRegitrar` to support this use case within a native image, as demonstrated in
the following example.
`@RegisterReflectionForBinding` can be applied to any Spring bean at the class level, but it can also be applied directly to a method, field, or constructor to better indicate where the hints are actually required.
The following example registers `Account` for serialization.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class TimestampConversionRuntimeHints implements RuntimeHintsRegistrar {
@Component
public class OrderService {
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
ReflectionHints reflectionHints = hints.reflection();
@RegisterReflectionForBinding(Account.class)
public void process(Order order) {
// ...
}
reflectionHints.registerTypeIfPresent(classLoader, "java.sql.Timestamp", hint -> hint
.withMethod("from", List.of(TypeReference.of(Instant.class)), ExecutableMode.INVOKE)
.onReachableType(TypeReference.of("java.sql.Timestamp")));
}
}
----
======
`TimestampConversionRuntimeHints` can then be registered declaratively via
<<aot.hints.import-runtime-hints>> or statically via a `META-INF/spring/aot.factories`
configuration file.
[NOTE]
====
The above `TimestampConversionRuntimeHints` class is a simplified version of the
`ObjectToObjectConverterRuntimeHints` class that is included in the framework and
registered by default.
Thus, this specific `Instant`-to-`Timestamp` use case is already handled by the framework.
====
[[aot.hints.testing]]
=== Testing Runtime Hints
Spring Core also ships `RuntimeHintsPredicates`, a utility for checking that existing hints match a particular use case.
This can be used in your own tests to validate that a `RuntimeHintsRegistrar` produces the expected results.
This can be used in your own tests to validate that a `RuntimeHintsRegistrar` contains the expected results.
We can write a test for our `SpellCheckService` and ensure that we will be able to load a dictionary at runtime:
include-code::./SpellCheckServiceTests[tag=hintspredicates]
@@ -753,7 +588,7 @@ If you forgot to contribute a hint, the test will fail and provide some details
[source,txt,indent=0,subs="verbatim,quotes"]
----
org.springframework.docs.core.aot.hints.testing.SampleReflection performReflection
INFO: Spring version: 6.2.0
INFO: Spring version:6.0.0-SNAPSHOT
Missing <"ReflectionHints"> for invocation <java.lang.Class#forName>
with arguments ["org.springframework.core.SpringVersion",
@@ -1,3 +1,7 @@
[[appendix]]
= Appendix
:page-section-summary-toc: 1
@@ -12,6 +12,7 @@ Spring's extensible XML configuration mechanism is based on XML Schema. If you a
familiar with Spring's current XML configuration extensions that come with the standard
Spring distribution, you should first read the previous section on xref:core/appendix/xsd-schemas.adoc[XML Schemas].
To create new XML configuration extensions:
. xref:core/appendix/xml-custom.adoc#core.appendix.xsd-custom-schema[Author] an XML schema to describe your custom element(s).
@@ -37,6 +38,7 @@ examples follow later in this appendix. The intent of this first simple example
through the basic steps of making a custom extension.)
[[xsd-custom-schema]]
== Authoring the Schema
@@ -108,6 +110,7 @@ can use autocompletion to let a user choose between several configuration option
defined in the enumeration.
[[xsd-custom-namespacehandler]]
== Coding a `NamespaceHandler`
@@ -142,7 +145,7 @@ use the `NamespaceHandlerSupport` class:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package org.springframework.samples.xml;
@@ -158,7 +161,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package org.springframework.samples.xml
@@ -184,6 +187,7 @@ means that each `BeanDefinitionParser` contains only the logic for parsing a sin
custom element, as we can see in the next step.
[[xsd-custom-parser]]
== Using `BeanDefinitionParser`
@@ -198,7 +202,7 @@ we can parse our custom XML content, as you can see in the following example:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package org.springframework.samples.xml;
@@ -236,7 +240,7 @@ single `BeanDefinition` represents.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package org.springframework.samples.xml
@@ -272,11 +276,13 @@ the basic grunt work of creating a single `BeanDefinition`.
single `BeanDefinition` represents.
======
In this simple case, this is all that we need to do. The creation of our single
`BeanDefinition` is handled by the `AbstractSingleBeanDefinitionParser` superclass, as
is the extraction and setting of the bean definition's unique identifier.
[[xsd-custom-registration]]
== Registering the Handler and the Schema
@@ -288,6 +294,7 @@ can, for example, be distributed alongside your binary classes in a JAR file. Th
XML parsing infrastructure automatically picks up your new extension by consuming
these special properties files, the formats of which are detailed in the next two sections.
[[xsd-custom-registration-spring-handlers]]
=== Writing `META-INF/spring.handlers`
@@ -306,6 +313,7 @@ The first part (the key) of the key-value pair is the URI associated with your c
namespace extension and needs to exactly match exactly the value of the `targetNamespace`
attribute, as specified in your custom XSD schema.
[[xsd-custom-registration-spring-schemas]]
=== Writing 'META-INF/spring.schemas'
@@ -329,6 +337,7 @@ You are encouraged to deploy your XSD file (or files) right alongside
the `NamespaceHandler` and `BeanDefinitionParser` classes on the classpath.
[[xsd-custom-using]]
== Using a Custom Extension in Your Spring XML Configuration
@@ -362,11 +371,13 @@ in a Spring XML configuration file:
<1> Our custom bean.
[[xsd-custom-meat]]
== More Detailed Examples
This section presents some more detailed examples of custom XML extensions.
[[xsd-custom-custom-nested]]
=== Nesting Custom Elements within Custom Elements
@@ -405,7 +416,7 @@ The following listing shows the `Component` class:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package com.foo;
@@ -438,7 +449,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package com.foo
@@ -469,7 +480,7 @@ setter property for the `components` property. The following listing shows such
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package com.foo;
@@ -511,7 +522,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package com.foo
@@ -587,7 +598,7 @@ we then create a custom `NamespaceHandler`:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package com.foo;
@@ -603,7 +614,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package com.foo
@@ -626,7 +637,7 @@ listing shows our custom `BeanDefinitionParser` implementation:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package com.foo;
@@ -677,7 +688,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package com.foo
@@ -742,6 +753,7 @@ http\://www.foo.example/schema/component=com.foo.ComponentNamespaceHandler
http\://www.foo.example/schema/component/component.xsd=com/foo/component.xsd
----
[[xsd-custom-custom-just-attributes]]
=== Custom Attributes on "`Normal`" Elements
@@ -775,7 +787,7 @@ JCache-initializing `BeanDefinition`. The following listing shows our `JCacheIni
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package com.foo;
@@ -795,7 +807,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package com.foo
@@ -831,7 +843,7 @@ Next, we need to create the associated `NamespaceHandler`, as follows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package com.foo;
@@ -849,7 +861,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package com.foo
@@ -874,7 +886,7 @@ The following listing shows our `BeanDefinitionDecorator` implementation:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package com.foo;
@@ -930,7 +942,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package com.foo
@@ -995,3 +1007,5 @@ http\://www.foo.example/schema/jcache=com.foo.JCacheNamespaceHandler
# in 'META-INF/spring.schemas'
http\://www.foo.example/schema/jcache/jcache.xsd=com/foo/jcache.xsd
----
@@ -4,6 +4,7 @@
This part of the appendix lists XML schemas related to the core container.
[[xsd-schemas-util]]
== The `util` Schema
@@ -28,6 +29,7 @@ correct schema so that the tags in the `util` namespace are available to you):
</beans>
----
[[xsd-schemas-util-constant]]
=== Using `<util:constant/>`
@@ -119,7 +121,7 @@ The following example enumeration shows how easy injecting an enum value is:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package jakarta.persistence;
@@ -132,7 +134,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package jakarta.persistence
@@ -150,7 +152,7 @@ Now consider the following setter of type `PersistenceContextType` and the corre
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package example;
@@ -166,7 +168,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package example
@@ -184,6 +186,7 @@ Kotlin::
</bean>
----
[[xsd-schemas-util-property-path]]
=== Using `<util:property-path/>`
@@ -305,6 +308,7 @@ You can specifically set the result type in the actual definition. This is not n
for most use cases, but it can sometimes be useful. See the javadoc for more info on
this feature.
[[xsd-schemas-util-properties]]
=== Using `<util:properties/>`
@@ -330,6 +334,7 @@ The following example uses a `util:properties` element to make a more concise re
<util:properties id="jdbcConfiguration" location="classpath:com/foo/jdbc-production.properties"/>
----
[[xsd-schemas-util-list]]
=== Using `<util:list/>`
@@ -384,6 +389,7 @@ following configuration:
If no `list-class` attribute is supplied, the container chooses a `List` implementation.
[[xsd-schemas-util-map]]
=== Using `<util:map/>`
@@ -438,6 +444,7 @@ following configuration:
If no `'map-class'` attribute is supplied, the container chooses a `Map` implementation.
[[xsd-schemas-util-set]]
=== Using `<util:set/>`
@@ -493,6 +500,7 @@ following configuration:
If no `set-class` attribute is supplied, the container chooses a `Set` implementation.
[[xsd-schemas-aop]]
== The `aop` Schema
@@ -522,6 +530,7 @@ are available to you):
----
[[xsd-schemas-context]]
== The `context` Schema
@@ -546,6 +555,7 @@ available to you:
</beans>
----
[[xsd-schemas-context-pphc]]
=== Using `<property-placeholder/>`
@@ -589,25 +599,34 @@ element for that purpose. Similarly, Spring's
xref:integration/cache/annotations.adoc[caching annotations] need to be explicitly
xref:integration/cache/annotations.adoc#cache-annotation-enable[enabled] as well.
[[xsd-schemas-context-component-scan]]
=== Using `<component-scan/>`
This element is detailed in the section on xref:core/beans/annotation-config.adoc[annotation-based container configuration].
This element is detailed in the section on xref:core/beans/annotation-config.adoc[annotation-based container configuration]
.
[[xsd-schemas-context-ltw]]
=== Using `<load-time-weaver/>`
This element is detailed in the section on xref:core/aop/using-aspectj.adoc#aop-aj-ltw[load-time weaving with AspectJ in the Spring Framework].
This element is detailed in the section on xref:core/aop/using-aspectj.adoc#aop-aj-ltw[load-time weaving with AspectJ in the Spring Framework]
.
[[xsd-schemas-context-sc]]
=== Using `<spring-configured/>`
This element is detailed in the section on xref:core/aop/using-aspectj.adoc#aop-atconfigurable[using AspectJ to dependency inject domain objects with Spring].
This element is detailed in the section on xref:core/aop/using-aspectj.adoc#aop-atconfigurable[using AspectJ to dependency inject domain objects with Spring]
.
[[xsd-schemas-context-mbe]]
=== Using `<mbean-export/>`
This element is detailed in the section on xref:integration/jmx/naming.adoc#jmx-context-mbeanexport[configuring annotation-based MBean export].
This element is detailed in the section on xref:integration/jmx/naming.adoc#jmx-context-mbeanexport[configuring annotation-based MBean export]
.
[[xsd-schemas-beans]]
@@ -647,3 +666,7 @@ as it stands).
In the case of the preceding example, you could assume that there is some logic that consumes
the bean definition and sets up some caching infrastructure that uses the supplied metadata.
@@ -3,3 +3,7 @@
:page-section-summary-toc: 1
This chapter covers Spring's Inversion of Control (IoC) container.
@@ -20,7 +20,7 @@ can be found in the xref:core/beans/standard-annotations.adoc[relevant section].
[NOTE]
====
Annotation injection is performed before external property injection. Thus, external
configuration (for example, XML-specified bean properties) effectively overrides the annotations
configuration (e.g. XML-specified bean properties) effectively overrides the annotations
for properties when wired through mixed approaches.
====
@@ -62,3 +62,6 @@ application context in which it is defined. This means that, if you put
it only checks for `@Autowired` beans in your controllers, and not your services. See
xref:web/webmvc/mvc-servlet.adoc[The DispatcherServlet] for more information.
====
@@ -1,5 +1,5 @@
[[beans-autowired-annotation-primary]]
= Fine-tuning Annotation-based Autowiring with `@Primary` or `@Fallback`
= Fine-tuning Annotation-based Autowiring with `@Primary`
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
@@ -15,7 +15,7 @@ primary `MovieCatalog`:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class MovieConfiguration {
@@ -33,7 +33,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class MovieConfiguration {
@@ -50,57 +50,14 @@ Kotlin::
----
======
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:
With the preceding configuration, the following `MovieRecommender` is autowired with the
`firstMovieCatalog`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
public class MovieConfiguration {
@Bean
public MovieCatalog firstMovieCatalog() { ... }
@Bean
@Fallback
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
@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]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@@ -113,7 +70,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@@ -152,3 +109,6 @@ The corresponding bean definitions follow:
</beans>
----
@@ -1,20 +1,19 @@
[[beans-autowired-annotation-qualifiers]]
= Fine-tuning Annotation-based Autowiring with Qualifiers
`@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:
`@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:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@@ -28,7 +27,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@@ -50,7 +49,7 @@ method parameters, as shown in the following example:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@@ -71,7 +70,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@@ -158,31 +157,44 @@ for a non-unique dependency situation, Spring matches the injection point name
the same-named candidate, if any (either by bean name or by associated alias).
Since version 6.1, this requires the `-parameters` Java compiler flag to be present.
As of 6.2, the container applies fast shortcut resolution for bean name matches,
bypassing the full type matching algorithm when the parameter name matches the
bean name and no type, qualifier or primary conditions override the match. It is
therefore recommendable for your parameter names to match the target bean names.
====
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
different semantics: after selecting candidate beans by type, the specified `String`
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).
For beans that are themselves defined as a collection, `Map`, or array type, `@Resource`
is a fine solution, referring to the specific collection or array bean by unique name.
That said, you can match collection, `Map`, and array types through Spring's
That said, as of 4.3, you can match collection, `Map`, and array types through Spring's
`@Autowired` type matching algorithm as well, as long as the element type information
is preserved in `@Bean` return type signatures or collection inheritance hierarchies.
In this case, you can use qualifier values to select among same-typed collections,
as outlined in the previous paragraph.
`@Autowired` also considers self references for injection (that is, references back to
the bean that is currently injected). See
xref:core/beans/annotation-config/autowired.adoc#beans-autowired-annotation-self-injection[Self Injection]
for details.
As of 4.3, `@Autowired` also considers self references for injection (that is, references
back to the bean that is currently injected). Note that self injection is a fallback.
Regular dependencies on other components always have precedence. In that sense, self
references do not participate in regular candidate selection and are therefore in
particular never primary. On the contrary, they always end up as lowest precedence.
In practice, you should use self references as a last resort only (for example, for
calling other methods on the same instance through the bean's transactional proxy).
Consider factoring out the affected methods to a separate delegate bean in such a scenario.
Alternatively, you can use `@Resource`, which may obtain a proxy back to the current bean
by its unique name.
[NOTE]
====
Trying to inject the results from `@Bean` methods on the same configuration class is
effectively a self-reference scenario as well. Either lazily resolve such references
in the method signature where it is actually needed (as opposed to an autowired field
in the configuration class) or declare the affected `@Bean` methods as `static`,
decoupling them from the containing configuration class instance and its lifecycle.
Otherwise, such beans are only considered in the fallback phase, with matching beans
on other configuration classes selected as primary candidates instead (if available).
====
`@Autowired` applies to fields, constructors, and multi-argument methods, allowing for
narrowing through qualifier annotations at the parameter level. In contrast, `@Resource`
@@ -198,7 +210,7 @@ provide the `@Qualifier` annotation within your definition, as the following exa
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@@ -211,7 +223,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@@ -229,7 +241,7 @@ following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@@ -250,7 +262,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@@ -322,7 +334,7 @@ the simple annotation, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@@ -333,7 +345,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@@ -351,7 +363,7 @@ following example:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@@ -366,7 +378,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@@ -406,7 +418,7 @@ consider the following annotation definition:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@@ -421,7 +433,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@@ -438,7 +450,7 @@ In this case `Format` is an enum, defined as follows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public enum Format {
VHS, DVD, BLURAY
@@ -447,7 +459,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
enum class Format {
VHS, DVD, BLURAY
@@ -464,7 +476,7 @@ for both attributes: `genre` and `format`, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@@ -490,7 +502,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@@ -568,3 +580,6 @@ the following example:
</beans>
----
--
@@ -13,7 +13,7 @@ You can apply the `@Autowired` annotation to constructors, as the following exam
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@@ -30,7 +30,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender @Autowired constructor(
private val customerPreferenceDao: CustomerPreferenceDao)
@@ -54,7 +54,7 @@ as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
@@ -71,7 +71,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SimpleMovieLister {
@@ -91,7 +91,7 @@ arguments, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@@ -112,7 +112,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@@ -139,7 +139,7 @@ following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@@ -159,7 +159,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender @Autowired constructor(
private val customerPreferenceDao: CustomerPreferenceDao) {
@@ -186,37 +186,14 @@ implementation type, consider declaring the most specific return type on your fa
method (at least as specific as required by the injection points referring to your bean).
====
.[[beans-autowired-annotation-self-injection]]Self Injection
****
`@Autowired` also considers self references for injection (that is, references back to
the bean that is currently injected).
Note, however, that self injection is a fallback mechanism. Regular dependencies on other
components always have precedence. In that sense, self references do not participate in
regular autowiring candidate selection and are therefore in particular never primary. On
the contrary, they always end up as lowest precedence.
In practice, you should use self references as a last resort only for example, for
calling other methods on the same instance through the bean's transactional proxy. As an
alternative, consider factoring out the affected methods to a separate delegate bean in
such a scenario.
Another alternative is to use `@Resource`, which may obtain a proxy back to the current
bean by its unique name.
======
[NOTE]
====
Trying to inject the results from `@Bean` methods in the same `@Configuration` class is
effectively a self-reference scenario as well. Either lazily resolve such references
in the method signature where it is actually needed (as opposed to an autowired field
in the configuration class) or declare the affected `@Bean` methods as `static`,
decoupling them from the containing configuration class instance and its lifecycle.
Otherwise, such beans are only considered in the fallback phase, with matching beans
on other configuration classes selected as primary candidates instead (if available).
As of 4.3, `@Autowired` also considers self references for injection (that is, references
back to the bean that is currently injected). Note that self injection is a fallback.
In practice, you should use self references as a last resort only (for example, for
calling other methods on the same instance through the bean's transactional proxy).
Consider factoring out the affected methods to a separate delegate bean in such a scenario.
====
======
****
You can also instruct Spring to provide all beans of a particular type from the
`ApplicationContext` by adding the `@Autowired` annotation to a field or method that
@@ -226,7 +203,7 @@ expects an array of that type, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@@ -239,7 +216,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@@ -257,7 +234,7 @@ The same applies for typed collections, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@@ -274,7 +251,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@@ -319,7 +296,7 @@ corresponding bean names, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@@ -336,7 +313,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@@ -361,7 +338,7 @@ non-required (i.e., by setting the `required` attribute in `@Autowired` to `fals
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
@@ -378,7 +355,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SimpleMovieLister {
@@ -444,15 +421,15 @@ through Java 8's `java.util.Optional`, as the following example shows:
}
----
You can also use a parameter-level `@Nullable` annotation (of any kind in any package --
for example, `javax.annotation.Nullable` from JSR-305) or just leverage Kotlin built-in
null-safety support:
As of Spring Framework 5.0, you can also use a `@Nullable` annotation (of any kind
in any package -- for example, `javax.annotation.Nullable` from JSR-305) or just leverage
Kotlin built-in null-safety support:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
@@ -465,7 +442,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SimpleMovieLister {
@@ -477,13 +454,6 @@ Kotlin::
----
======
[NOTE]
====
A type-level `@Nullable` annotation such as from JSpecify is not supported in Spring
Framework 6.2 yet. You need to upgrade to Spring Framework 7.0 where the framework
detects type-level annotations and consistently declares JSpecify in its own codebase.
====
You can also use `@Autowired` for interfaces that are well-known resolvable
dependencies: `BeanFactory`, `ApplicationContext`, `Environment`, `ResourceLoader`,
`ApplicationEventPublisher`, and `MessageSource`. These interfaces and their extended
@@ -495,7 +465,7 @@ an `ApplicationContext` object:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@@ -511,7 +481,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@@ -530,3 +500,6 @@ The `@Autowired`, `@Inject`, `@Value`, and `@Resource` annotations are handled b
within your own `BeanPostProcessor` or `BeanFactoryPostProcessor` types (if any).
These types must be 'wired up' explicitly by using XML or a Spring `@Bean` method.
====
@@ -28,3 +28,6 @@ with the `CustomAutowireConfigurer`
When multiple beans qualify as autowire candidates, the determination of a "`primary`" is
as follows: If exactly one bean definition among the candidates has a `primary`
attribute set to `true`, it is selected.
@@ -9,7 +9,7 @@ configuration:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class MyConfiguration {
@@ -28,7 +28,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class MyConfiguration {
@@ -50,7 +50,7 @@ used as a qualifier, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
@@ -61,7 +61,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Autowired
private lateinit var s1: Store<String> // <String> qualifier, injects the stringStore bean
@@ -78,7 +78,7 @@ following example autowires a generic `List`:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@@ -88,7 +88,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@@ -96,3 +96,6 @@ Kotlin::
private lateinit var s: List<Store<Integer>>
----
======
@@ -17,7 +17,7 @@ cleared upon destruction:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class CachingMovieLister {
@@ -35,7 +35,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class CachingMovieLister {
@@ -64,3 +64,7 @@ JDK 11. As of Jakarta EE 9, the package lives in `jakarta.annotation` now. If ne
the `jakarta.annotation-api` artifact needs to be obtained via Maven Central now,
simply to be added to the application's classpath like any other library.
====
@@ -15,7 +15,7 @@ as demonstrated in the following example:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
@@ -31,7 +31,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SimpleMovieLister {
@@ -54,7 +54,7 @@ named `movieFinder` injected into its setter method:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
@@ -69,7 +69,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SimpleMovieLister {
@@ -103,7 +103,7 @@ named "customerPreferenceDao" and then falls back to a primary type match for th
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@@ -124,7 +124,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@@ -142,3 +142,4 @@ Kotlin::
`ApplicationContext`.
======
--
@@ -7,22 +7,22 @@
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
public class MovieRecommender {
@Component
public class MovieRecommender {
private final String catalog;
private final String catalog;
public MovieRecommender(@Value("${catalog.name}") String catalog) {
this.catalog = catalog;
}
}
public MovieRecommender(@Value("${catalog.name}") String catalog) {
this.catalog = catalog;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
class MovieRecommender(@Value("\${catalog.name}") private val catalog: String)
@@ -35,16 +35,16 @@ With the following configuration:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }
@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@PropertySource("classpath:application.properties")
@@ -56,7 +56,7 @@ And the following `application.properties` file:
[source,java,indent=0,subs="verbatim,quotes"]
----
catalog.name=MovieCatalog
catalog.name=MovieCatalog
----
In that case, the `catalog` parameter and field will be equal to the `MovieCatalog` value.
@@ -71,7 +71,7 @@ example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class AppConfig {
@@ -85,7 +85,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class AppConfig {
@@ -101,11 +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()`, `setValueSeparator()`, or
`setEscapeCharacter()` to customize the placeholder syntax. In addition, the default
escape character can be changed or disabled globally by setting the
`spring.placeholder.escapeCharacter.default` property via a JVM system property (or via
the xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism).
`setPlaceholderPrefix`, `setPlaceholderSuffix`, or `setValueSeparator` to customize
placeholders.
NOTE: Spring Boot configures by default a `PropertySourcesPlaceholderConfigurer` bean that
will get properties from `application.properties` and `application.yml` files.
@@ -120,22 +117,22 @@ It is possible to provide a default value as following:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
public class MovieRecommender {
@Component
public class MovieRecommender {
private final String catalog;
private final String catalog;
public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) {
this.catalog = catalog;
}
}
public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) {
this.catalog = catalog;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
class MovieRecommender(@Value("\${catalog.name:defaultCatalog}") private val catalog: String)
@@ -151,23 +148,23 @@ provide conversion support for your own custom type, you can provide your own
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class AppConfig {
@Configuration
public class AppConfig {
@Bean
public ConversionService conversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
conversionService.addConverter(new MyCustomConverter());
return conversionService;
}
}
@Bean
public ConversionService conversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
conversionService.addConverter(new MyCustomConverter());
return conversionService;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class AppConfig {
@@ -189,22 +186,22 @@ computed at runtime as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
public class MovieRecommender {
@Component
public class MovieRecommender {
private final String catalog;
private final String catalog;
public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) {
this.catalog = catalog;
}
}
public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) {
this.catalog = catalog;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
class MovieRecommender(
@@ -218,26 +215,28 @@ SpEL also enables the use of more complex data structures:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
public class MovieRecommender {
@Component
public class MovieRecommender {
private final Map<String, Integer> countOfMoviesPerCatalog;
private final Map<String, Integer> countOfMoviesPerCatalog;
public MovieRecommender(
@Value("#{{'Thriller': 100, 'Comedy': 300}}") Map<String, Integer> countOfMoviesPerCatalog) {
this.countOfMoviesPerCatalog = countOfMoviesPerCatalog;
}
}
public MovieRecommender(
@Value("#{{'Thriller': 100, 'Comedy': 300}}") Map<String, Integer> countOfMoviesPerCatalog) {
this.countOfMoviesPerCatalog = countOfMoviesPerCatalog;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
class MovieRecommender(
@Value("#{{'Thriller': 100, 'Comedy': 300}}") private val countOfMoviesPerCatalog: Map<String, Int>)
----
======
@@ -29,6 +29,7 @@ created and initialized, you have a fully configured and executable system or ap
image::container-magic.png[]
[[beans-factory-metadata]]
== Configuration Metadata
@@ -62,6 +63,8 @@ Typically, one does not configure fine-grained domain objects in the container,
it is usually the responsibility of repositories and business logic to create and load
domain objects.
[[beans-factory-xml]]
=== XML as an External Configuration DSL
@@ -107,16 +110,16 @@ as the local file system, the Java `CLASSPATH`, and so on.
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
----
======
@@ -185,6 +188,7 @@ definition. This linkage between `id` and `ref` elements expresses the dependenc
collaborating objects. For details of configuring an object's dependencies, see
xref:core/beans/dependencies.adoc[Dependencies].
[[beans-factory-xml-import]]
=== Composing XML-based Configuration Metadata
@@ -240,6 +244,7 @@ The namespace itself provides the import directive feature. Further
configuration features beyond plain bean definitions are available in a selection
of XML namespaces provided by Spring -- for example, the `context` and `util` namespaces.
[[beans-factory-groovy]]
=== The Groovy Bean Definition DSL
@@ -274,6 +279,7 @@ supports Spring's XML configuration namespaces. It also allows for importing XML
bean definition files through an `importBeans` directive.
[[beans-factory-client]]
== Using the Container
@@ -288,7 +294,7 @@ example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
@@ -302,18 +308,18 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
import org.springframework.beans.factory.getBean
import org.springframework.beans.factory.getBean
// create and configure beans
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
// retrieve configured instance
val service = context.getBean<PetStoreService>("petStore")
// retrieve configured instance
val service = context.getBean<PetStoreService>("petStore")
// use configured instance
var userList = service.getUsernameList()
// use configured instance
var userList = service.getUsernameList()
----
======
@@ -325,14 +331,14 @@ The following example shows Groovy configuration:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val context = GenericGroovyApplicationContext("services.groovy", "daos.groovy")
----
@@ -346,7 +352,7 @@ example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
@@ -355,7 +361,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val context = GenericApplicationContext()
XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml")
@@ -370,7 +376,7 @@ example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
@@ -379,7 +385,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val context = GenericApplicationContext()
GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy")
@@ -397,3 +403,6 @@ code should never use them. Indeed, your application code should have no calls t
Spring's integration with web frameworks provides dependency injection for various web
framework components such as controllers and JSF-managed beans, letting you declare
a dependency on a specific bean through metadata (such as an autowiring annotation).
@@ -21,6 +21,7 @@ operate on shared `BeanDefinition` objects as a core metadata representation.
This is the essence of what makes Spring's container so flexible and extensible.
[[context-introduction-ctx-vs-beanfactory]]
== `BeanFactory` or `ApplicationContext`?
@@ -89,7 +90,7 @@ you need to programmatically call `addBeanPostProcessor`, as the following examp
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// populate the factory with bean definitions
@@ -103,7 +104,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val factory = DefaultListableBeanFactory()
// populate the factory with bean definitions
@@ -123,7 +124,7 @@ you need to call its `postProcessBeanFactory` method, as the following example s
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
@@ -139,7 +140,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val factory = DefaultListableBeanFactory()
val reader = XmlBeanDefinitionReader(factory)
@@ -78,3 +78,7 @@ important (at least for singleton beans) that if you have a (parent) bean defini
which you intend to use only as a template, and this definition specifies a class, you
must make sure to set the __abstract__ attribute to __true__, otherwise the application
context will actually (attempt to) pre-instantiate the `abstract` bean.
@@ -1,29 +1,28 @@
[[beans-classpath-scanning]]
= Classpath Scanning and Managed Components
Most examples in this chapter use XML to specify the configuration metadata that
produces each `BeanDefinition` within the Spring container. The previous section
(xref:core/beans/annotation-config.adoc[Annotation-based Container Configuration])
demonstrates how to provide a lot of the configuration metadata through source-level
annotations. Even in those examples, however, the "base" bean definitions are explicitly
defined in the XML file, while the annotations drive only the dependency injection.
This section describes an option for implicitly detecting the candidate components by
scanning the classpath. Candidate components are classes that match against a filter
criteria and have a corresponding bean definition registered with the container.
This removes the need to use XML to perform bean registration. Instead, you can use
annotations (for example, `@Component`), AspectJ type expressions, or your own
Most examples in this chapter use XML to specify the configuration metadata that produces
each `BeanDefinition` within the Spring container. The previous section
(xref:core/beans/annotation-config.adoc[Annotation-based Container Configuration]) demonstrates how to provide a lot of the configuration
metadata through source-level annotations. Even in those examples, however, the "base"
bean definitions are explicitly defined in the XML file, while the annotations drive only
the dependency injection. This section describes an option for implicitly detecting the
candidate components by scanning the classpath. Candidate components are classes that
match against a filter criteria and have a corresponding bean definition registered with
the container. This removes the need to use XML to perform bean registration. Instead, you
can use annotations (for example, `@Component`), AspectJ type expressions, or your own
custom filter criteria to select which classes have bean definitions registered with
the container.
[NOTE]
====
You can define beans using Java rather than using XML files. Take a look at the
`@Configuration`, `@Bean`, `@Import`, and `@DependsOn` annotations for examples
of how to use these features.
`@Configuration`, `@Bean`, `@Import`, and `@DependsOn` annotations for examples of how to
use these features.
====
[[beans-stereotype-annotations]]
== `@Component` and Further Stereotype Annotations
@@ -47,6 +46,7 @@ clearly the better choice. Similarly, as stated earlier, `@Repository` is alread
supported as a marker for automatic exception translation in your persistence layer.
[[beans-meta-annotations]]
== Using Meta-annotations and Composed Annotations
@@ -59,7 +59,7 @@ is meta-annotated with `@Component`, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@@ -74,7 +74,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@@ -103,7 +103,7 @@ customization of the `proxyMode`. The following listing shows the definition of
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@@ -123,7 +123,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@@ -142,7 +142,7 @@ You can then use `@SessionScope` without declaring the `proxyMode` as follows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Service
@SessionScope
@@ -153,7 +153,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Service
@SessionScope
@@ -169,7 +169,7 @@ You can also override the value for the `proxyMode`, as the following example sh
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
@@ -180,7 +180,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
@@ -195,6 +195,7 @@ For further details, see the
wiki page.
[[beans-scanning-autodetection]]
== Automatically Detecting Classes and Registering Bean Definitions
@@ -206,7 +207,7 @@ are eligible for such autodetection:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Service
public class SimpleMovieLister {
@@ -221,7 +222,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Service
class SimpleMovieLister(private val movieFinder: MovieFinder)
@@ -232,7 +233,7 @@ Kotlin::
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Repository
public class JpaMovieFinder implements MovieFinder {
@@ -242,7 +243,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Repository
class JpaMovieFinder : MovieFinder {
@@ -261,7 +262,7 @@ comma- or semicolon- or space-separated list that includes the parent package of
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@ComponentScan(basePackages = "org.example")
@@ -272,7 +273,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@ComponentScan(basePackages = ["org.example"])
@@ -333,6 +334,7 @@ NOTE: You can disable the registration of `AutowiredAnnotationBeanPostProcessor`
with a value of `false`.
[[beans-scanning-filters]]
== Using Filters to Customize Scanning
@@ -378,7 +380,7 @@ and using "`stub`" repositories instead:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@ComponentScan(basePackages = "org.example",
@@ -391,7 +393,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@ComponentScan(basePackages = ["org.example"],
@@ -424,6 +426,7 @@ annotated or meta-annotated with `@Component`, `@Repository`, `@Service`, `@Cont
`@RestController`, or `@Configuration`.
[[beans-factorybeans-annotations]]
== Defining Bean Metadata within Components
@@ -435,7 +438,7 @@ annotated classes. The following example shows how to do so:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
public class FactoryMethodComponent {
@@ -454,7 +457,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
class FactoryMethodComponent {
@@ -494,7 +497,7 @@ support for autowiring of `@Bean` methods. The following example shows how to do
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
public class FactoryMethodComponent {
@@ -533,7 +536,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
class FactoryMethodComponent {
@@ -586,7 +589,7 @@ The following example shows how to use `InjectionPoint`:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
public class FactoryMethodComponent {
@@ -600,7 +603,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
class FactoryMethodComponent {
@@ -660,6 +663,7 @@ analogous to how the container selects between multiple `@Autowired` constructor
====
[[beans-scanning-name-generator]]
== Naming Autodetected Components
@@ -699,7 +703,7 @@ following component classes were detected, the names would be `myMovieLister` an
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Service("myMovieLister")
public class SimpleMovieLister {
@@ -709,7 +713,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Service("myMovieLister")
class SimpleMovieLister {
@@ -722,7 +726,7 @@ Kotlin::
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Repository
public class MovieFinderImpl implements MovieFinder {
@@ -732,7 +736,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Repository
class MovieFinderImpl : MovieFinder {
@@ -759,7 +763,7 @@ fully qualified class name for the generated bean name. The
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
@@ -770,7 +774,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@ComponentScan(basePackages = ["org.example"], nameGenerator = MyNameGenerator::class)
@@ -793,6 +797,7 @@ components may be making explicit references to it. On the other hand, the
auto-generated names are adequate whenever the container is responsible for wiring.
[[beans-scanning-scope-resolver]]
== Providing a Scope for Autodetected Components
@@ -805,7 +810,7 @@ scope within the annotation, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Scope("prototype")
@Repository
@@ -816,7 +821,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Scope("prototype")
@Repository
@@ -832,10 +837,10 @@ definitions, there is no notion of bean definition inheritance, and inheritance
hierarchies at the class level are irrelevant for metadata purposes.
For details on web-specific scopes such as "`request`" or "`session`" in a Spring context,
see xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other[Request, Session, Application, and WebSocket Scopes].
As with the pre-built annotations for those scopes, you may also compose your own scoping
annotations by using Spring's meta-annotation approach: for example, a custom annotation
meta-annotated with `@Scope("prototype")`, possibly also declaring a custom scoped-proxy mode.
see xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other[Request, Session, Application, and WebSocket Scopes]. As with the pre-built annotations for those scopes,
you may also compose your own scoping annotations by using Spring's meta-annotation
approach: for example, a custom annotation meta-annotated with `@Scope("prototype")`,
possibly also declaring a custom scoped-proxy mode.
NOTE: To provide a custom strategy for scope resolution rather than relying on the
annotation-based approach, you can implement the
@@ -848,7 +853,7 @@ an annotation and a bean definition shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
@@ -859,7 +864,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@ComponentScan(basePackages = ["org.example"], scopeResolver = MyScopeResolver::class)
@@ -877,8 +882,7 @@ Kotlin::
----
When using certain non-singleton scopes, it may be necessary to generate proxies for the
scoped objects. The reasoning is described in
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other-injection[Scoped Beans as Dependencies].
scoped objects. The reasoning is described in xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other-injection[Scoped Beans as Dependencies].
For this purpose, a scoped-proxy attribute is available on the component-scan
element. The three possible values are: `no`, `interfaces`, and `targetClass`. For example,
the following configuration results in standard JDK dynamic proxies:
@@ -887,7 +891,7 @@ the following configuration results in standard JDK dynamic proxies:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
@@ -898,7 +902,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@ComponentScan(basePackages = ["org.example"], scopedProxy = ScopedProxyMode.INTERFACES)
@@ -916,6 +920,7 @@ Kotlin::
----
[[beans-scanning-qualifiers]]
== Providing Qualifier Metadata with Annotations
@@ -934,7 +939,7 @@ technique:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
@Qualifier("Action")
@@ -945,7 +950,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
@Qualifier("Action")
@@ -957,7 +962,7 @@ Kotlin::
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
@Genre("Action")
@@ -968,7 +973,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
@Genre("Action")
@@ -982,7 +987,7 @@ Kotlin::
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
@Offline
@@ -993,7 +998,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
@Offline
@@ -1007,3 +1012,7 @@ NOTE: As with most annotation-based alternatives, keep in mind that the annotati
bound to the class definition itself, while the use of XML allows for multiple beans
of the same type to provide variations in their qualifier metadata, because that
metadata is provided per-instance rather than per-class.
@@ -24,6 +24,7 @@ package also provides the following functionality:
`HierarchicalBeanFactory` interface.
[[context-functionality-messagesource]]
== Internationalization using `MessageSource`
@@ -101,7 +102,7 @@ implementations and so can be cast to the `MessageSource` interface.
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
@@ -112,7 +113,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml")
@@ -160,7 +161,7 @@ converted into `String` objects and inserted into placeholders in the lookup mes
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class Example {
@@ -180,7 +181,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class Example {
@@ -223,7 +224,7 @@ argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public static void main(final String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
@@ -235,7 +236,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml")
@@ -272,6 +273,7 @@ See the {spring-framework-api}/context/support/ReloadableResourceBundleMessageSo
javadoc for details.
[[context-functionality-events]]
== Standard and Custom Events
@@ -342,7 +344,7 @@ simple class that extends Spring's `ApplicationEvent` base class:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class BlockedListEvent extends ApplicationEvent {
@@ -361,7 +363,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class BlockedListEvent(source: Any,
val address: String,
@@ -378,7 +380,7 @@ example shows such a class:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class EmailService implements ApplicationEventPublisherAware {
@@ -405,7 +407,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class EmailService : ApplicationEventPublisherAware {
@@ -445,7 +447,7 @@ shows such a class:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {
@@ -463,7 +465,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class BlockedListNotifier : ApplicationListener<BlockedListEvent> {
@@ -482,7 +484,7 @@ You can register as many event listeners as you wish, but note that, by default,
This means that the `publishEvent()` method blocks until all listeners have finished processing the event.
One advantage of this synchronous and single-threaded approach is that, when a listener receives an event,
it operates inside the transaction context of the publisher if a transaction context is available.
If another strategy for event publication becomes necessary, for example, asynchronous event processing by default,
If another strategy for event publication becomes necessary, e.g. asynchronous event processing by default,
see the javadoc for Spring's {spring-framework-api}/context/event/ApplicationEventMulticaster.html[`ApplicationEventMulticaster`] interface
and {spring-framework-api}/context/event/SimpleApplicationEventMulticaster.html[`SimpleApplicationEventMulticaster`] implementation
for configuration options which can be applied to a custom "applicationEventMulticaster" bean definition.
@@ -511,7 +513,7 @@ the classes above:
<property name="notificationAddress" value="blockedlist@example.org"/>
</bean>
<!-- optional: a custom ApplicationEventMulticaster definition -->
<!-- optional: a custom ApplicationEventMulticaster definition -->
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="..."/>
<property name="errorHandler" ref="..."/>
@@ -532,6 +534,7 @@ complete support for building lightweight,
https://www.enterpriseintegrationpatterns.com[pattern-oriented], event-driven
architectures that build upon the well-known Spring programming model.
[[context-functionality-events-annotation]]
=== Annotation-based Event Listeners
@@ -542,7 +545,7 @@ You can register an event listener on any method of a managed bean by using the
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class BlockedListNotifier {
@@ -561,7 +564,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class BlockedListNotifier {
@@ -575,8 +578,6 @@ Kotlin::
----
======
NOTE: Do not define such beans to be lazy as the `ApplicationContext` will honour that and will not register the method to listen to events.
The method signature once again declares the event type to which it listens,
but, this time, with a flexible name and without implementing a specific listener interface.
The event type can also be narrowed through generics as long as the actual event type
@@ -590,7 +591,7 @@ following example shows how to do so:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
@@ -600,7 +601,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@EventListener(ContextStartedEvent::class, ContextRefreshedEvent::class)
fun handleContextStart() {
@@ -620,7 +621,7 @@ The following example shows how our notifier can be rewritten to be invoked only
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blEvent) {
@@ -630,7 +631,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@EventListener(condition = "#blEvent.content == 'my-event'")
fun processBlockedListEvent(blEvent: BlockedListEvent) {
@@ -676,7 +677,7 @@ method signature to return the event that should be published, as the following
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@EventListener
public ListUpdateEvent handleBlockedListEvent(BlockedListEvent event) {
@@ -687,7 +688,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@EventListener
fun handleBlockedListEvent(event: BlockedListEvent): ListUpdateEvent {
@@ -704,6 +705,7 @@ The `handleBlockedListEvent()` method publishes a new `ListUpdateEvent` for ever
`BlockedListEvent` that it handles. If you need to publish several events, you can return
a `Collection` or an array of events instead.
[[context-functionality-events-async]]
=== Asynchronous Listeners
@@ -715,7 +717,7 @@ The following example shows how to do so:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@EventListener
@Async
@@ -726,7 +728,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@EventListener
@Async
@@ -750,6 +752,7 @@ Be aware of the following limitations when using asynchronous events:
See xref:integration/observability.adoc#observability.application-events[the `@EventListener` Observability section]
for more information on Observability concerns.
[[context-functionality-events-order]]
=== Ordering Listeners
@@ -760,7 +763,7 @@ annotation to the method declaration, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@EventListener
@Order(42)
@@ -771,7 +774,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@EventListener
@Order(42)
@@ -781,6 +784,7 @@ Kotlin::
----
======
[[context-functionality-events-generics]]
=== Generic Events
@@ -793,7 +797,7 @@ can create the following listener definition to receive only `EntityCreatedEvent
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
@@ -803,7 +807,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@EventListener
fun onPersonCreated(event: EntityCreatedEvent<Person>) {
@@ -825,7 +829,7 @@ environment provides. The following event shows how to do so:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
@@ -842,7 +846,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class EntityCreatedEvent<T>(entity: T) : ApplicationEvent(entity), ResolvableTypeProvider {
@@ -860,7 +864,7 @@ Finally, as with classic `ApplicationListener` implementations, the actual multi
happens via a context-wide `ApplicationEventMulticaster` at runtime. By default, this is a
`SimpleApplicationEventMulticaster` with synchronous event publication in the caller thread.
This can be replaced/customized through an "applicationEventMulticaster" bean definition,
for example, for processing all events asynchronously and/or for handling listener exceptions:
e.g. for processing all events asynchronously and/or for handling listener exceptions:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -874,6 +878,7 @@ for example, for processing all events asynchronously and/or for handling listen
----
[[context-functionality-resources]]
== Convenient Access to Low-level Resources
@@ -906,6 +911,7 @@ with special prefixes to force loading of definitions from the classpath or a UR
regardless of the actual context type.
[[context-functionality-startup]]
== Application Startup Tracking
@@ -930,7 +936,7 @@ Here is an example of instrumentation in the `AnnotationConfigApplicationContext
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// create a startup step and start recording
StartupStep scanPackages = getApplicationStartup().start("spring.context.base-packages.scan");
@@ -944,7 +950,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// create a startup step and start recording
val scanPackages = getApplicationStartup().start("spring.context.base-packages.scan")
@@ -982,7 +988,6 @@ or ask for the `ApplicationStartup` type on any injection point.
NOTE: Developers should not use the `"spring.*"` namespace when creating custom startup steps.
This namespace is reserved for internal Spring usage and is subject to change.
[[context-create]]
== Convenient ApplicationContext Instantiation for Web Applications
@@ -1015,6 +1020,7 @@ Examples are `/WEB-INF/{asterisk}Context.xml` (for all files with names that end
(for all such files in any subdirectory of `WEB-INF`).
[[context-deploy-rar]]
== Deploying a Spring `ApplicationContext` as a Jakarta EE RAR File
@@ -1045,8 +1051,7 @@ all application classes into a RAR file (which is a standard JAR file with a dif
file extension).
. Add all required library JARs into the root of the RAR archive.
. Add a
`META-INF/ra.xml` deployment descriptor (as shown in the
{spring-framework-api}/jca/context/SpringContextResourceAdapter.html[javadoc for `SpringContextResourceAdapter`])
`META-INF/ra.xml` deployment descriptor (as shown in the {spring-framework-api}/jca/context/SpringContextResourceAdapter.html[javadoc for `SpringContextResourceAdapter`])
and the corresponding Spring XML bean definition file(s) (typically
`META-INF/applicationContext.xml`).
. Drop the resulting RAR file into your
@@ -1059,3 +1064,7 @@ other modules. A RAR-based `ApplicationContext` may also, for example, schedule
or react to new files in the file system (or the like). If it needs to allow synchronous
access from the outside, it could (for example) export RMI endpoints, which may be used
by other application modules on the same machine.
@@ -11,7 +11,7 @@ To enable load-time weaving, you can add the `@EnableLoadTimeWeaving` to one of
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableLoadTimeWeaving
@@ -21,7 +21,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableLoadTimeWeaving
@@ -45,5 +45,8 @@ xref:data-access/orm/jpa.adoc[Spring's JPA support] where load-time weaving may
necessary for JPA class transformation.
Consult the
{spring-framework-api}/orm/jpa/LocalContainerEntityManagerFactoryBean.html[`LocalContainerEntityManagerFactoryBean`]
javadoc for more detail. For more on AspectJ load-time weaving, see
xref:core/aop/using-aspectj.adoc#aop-aj-ltw[Load-time Weaving with AspectJ in the Spring Framework].
javadoc for more detail. For more on AspectJ load-time weaving, see xref:core/aop/using-aspectj.adoc#aop-aj-ltw[Load-time Weaving with AspectJ in the Spring Framework].
@@ -57,9 +57,9 @@ The following table describes these properties:
In addition to bean definitions that contain information on how to create a specific
bean, the `ApplicationContext` implementations also permit the registration of existing
objects that are created outside the container (by users). This is done by accessing the
ApplicationContext's `BeanFactory` through the `getAutowireCapableBeanFactory()` method,
which returns the `DefaultListableBeanFactory` implementation. `DefaultListableBeanFactory`
supports this registration through the `registerSingleton(..)` and `registerBeanDefinition(..)`
ApplicationContext's `BeanFactory` through the `getBeanFactory()` method, which returns
the `DefaultListableBeanFactory` implementation. `DefaultListableBeanFactory` supports
this registration through the `registerSingleton(..)` and `registerBeanDefinition(..)`
methods. However, typical applications work solely with beans defined through regular
bean definition metadata.
@@ -74,23 +74,23 @@ lead to concurrent access exceptions, inconsistent state in the bean container,
====
[[beans-definition-overriding]]
== Overriding Beans
Bean overriding occurs when a bean is registered using an identifier that is already
allocated. While bean overriding is possible, it makes the configuration harder to read.
WARNING: Bean overriding will be deprecated in a future release.
Bean overriding is happening when a bean is registered using an identifier that is
already allocated. While bean overriding is possible, it makes the configuration harder
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 a setup, an
flag 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 attempt to override a bean 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`.
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
.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
@@ -98,9 +98,6 @@ a scanned bean class with the same component name as long as the return type of
the `@Bean` factory method in favor of any pre-declared constructor on the bean class.
****
NOTE: We acknowledge that overriding beans in test scenarios is convenient, and there is
explicit support for this as of Spring Framework 6.2. Please refer to
xref:testing/testcontext-framework/bean-overriding.adoc[this section] for more details.
[[beans-beanname]]
@@ -145,6 +142,7 @@ case when there is more than one character and both the first and second charact
are upper case, the original casing gets preserved. These are the same rules as
defined by `java.beans.Introspector.decapitalize` (which Spring uses here).
[[beans-beanname-alias]]
=== Aliasing a Bean outside the Bean Definition
@@ -194,6 +192,7 @@ See xref:core/beans/java/bean-annotation.adoc[Using the `@Bean` Annotation] for
****
[[beans-factory-class]]
== Instantiating Beans
@@ -205,8 +204,7 @@ If you use XML-based configuration metadata, you specify the type (or class) of
that is to be instantiated in the `class` attribute of the `<bean/>` element. This
`class` attribute (which, internally, is a `Class` property on a `BeanDefinition`
instance) is usually mandatory. (For exceptions, see
xref:core/beans/definition.adoc#beans-factory-class-instance-factory-method[Instantiation by Using an Instance Factory Method]
and xref:core/beans/child-bean-definitions.adoc[Bean Definition Inheritance].)
xref:core/beans/definition.adoc#beans-factory-class-instance-factory-method[Instantiation by Using an Instance Factory Method] and xref:core/beans/child-bean-definitions.adoc[Bean Definition Inheritance].)
You can use the `Class` property in one of two ways:
* Typically, to specify the bean class to be constructed in the case where the container
@@ -230,6 +228,7 @@ a bean definition would be `com.example.SomeThing$OtherThing` or
`com.example.SomeThing.OtherThing`.
****
[[beans-factory-class-ctor]]
=== Instantiation with a Constructor
@@ -264,6 +263,7 @@ NOTE: In the case of constructor arguments, the container can select a correspon
constructor among several overloaded constructors. That said, to avoid ambiguities,
it is recommended to keep your constructor signatures as straightforward as possible.
[[beans-factory-class-static-factory-method]]
=== Instantiation with a Static Factory Method
@@ -293,7 +293,7 @@ The following example shows a class that would work with the preceding bean defi
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class ClientService {
private static ClientService clientService = new ClientService();
@@ -307,7 +307,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class ClientService private constructor() {
companion object {
@@ -341,6 +341,7 @@ overloads of the `mock` method. Choose the most specific variant of `mock` possi
----
====
[[beans-factory-class-instance-factory-method]]
=== Instantiation by Using an Instance Factory Method
@@ -372,7 +373,7 @@ The following example shows the corresponding class:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class DefaultServiceLocator {
@@ -386,7 +387,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class DefaultServiceLocator {
companion object {
@@ -422,7 +423,7 @@ The following example shows the corresponding class:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class DefaultServiceLocator {
@@ -442,7 +443,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class DefaultServiceLocator {
companion object {
@@ -472,6 +473,7 @@ xref:core/beans/definition.adoc#beans-factory-class-static-factory-method[static
`FactoryBean` (notice the capitalization) refers to a Spring-specific
xref:core/beans/factory-extension.adoc#beans-factory-extension-factorybean[`FactoryBean`] implementation class.
[[beans-factory-type-determination]]
=== Determining a Bean's Runtime Type
@@ -487,3 +489,5 @@ The recommended way to find out about the actual runtime type of a particular be
a `BeanFactory.getType` call for the specified bean name. This takes all of the above
cases into account and returns the type of object that a `BeanFactory.getBean` call is
going to return for the same bean name.
@@ -7,3 +7,6 @@ Spring parlance). Even the simplest application has a few objects that work toge
present what the end-user sees as a coherent application. This next section explains how
you go from defining a number of bean definitions that stand alone to a fully realized
application where objects collaborate to achieve a goal.
@@ -16,8 +16,7 @@ advantages:
during development, without negating the option of switching to explicit wiring when
the code base becomes more stable.
When using XML-based configuration metadata (see
xref:core/beans/dependencies/factory-collaborators.adoc[Dependency Injection]), you
When using XML-based configuration metadata (see xref:core/beans/dependencies/factory-collaborators.adoc[Dependency Injection]), you
can specify the autowire mode for a bean definition with the `autowire` attribute of the
`<bean/>` element. The autowiring functionality has four modes. You specify autowiring
per bean and can thus choose which ones to autowire. The following table describes the
@@ -61,6 +60,7 @@ 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
@@ -90,14 +90,14 @@ In the latter scenario, you have several options:
* Abandon autowiring in favor of explicit wiring.
* Avoid autowiring for a bean definition by setting its `autowire-candidate` attributes
to `false`, as described in the
xref:core/beans/dependencies/factory-autowire.adoc#beans-factory-autowire-candidate[next section].
to `false`, as described in the xref:core/beans/dependencies/factory-autowire.adoc#beans-factory-autowire-candidate[next section].
* Designate a single bean definition as the primary candidate by setting the
`primary` attribute of its `<bean/>` element to `true`.
* Implement the more fine-grained control available with annotation-based configuration,
as described in xref:core/beans/annotation-config.adoc[Annotation-based Container Configuration].
[[beans-factory-autowire-candidate]]
== Excluding a Bean from Autowiring
@@ -124,18 +124,5 @@ These techniques are useful for beans that you never want to be injected into ot
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.
[NOTE]
====
As of 6.2, `@Bean` methods support two variants of the autowire candidate flag:
`autowireCandidate` and `defaultCandidate`.
When using xref:core/beans/annotation-config/autowired-qualifiers.adoc[qualifiers],
a bean marked with `defaultCandidate=false` is only available for injection points
where an additional qualifier indication is present. This is useful for restricted
delegates that are supposed to be injectable in certain areas but are not meant to
get in the way of beans of the same type in other places. Such a bean will never
get injected by plain declared type only, rather by type plus specific qualifier.
In contrast, `autowireCandidate=false` behaves exactly like the `autowire-candidate`
attribute as explained above: Such a bean will never get injected by type at all.
====
@@ -16,9 +16,8 @@ not know the location or class of the dependencies. As a result, your classes be
to test, particularly when the dependencies are on interfaces or abstract base classes,
which allow for stub or mock implementations to be used in unit tests.
DI exists in two major variants:
xref:core/beans/dependencies/factory-collaborators.adoc#beans-constructor-injection[Constructor-based dependency injection]
and xref:core/beans/dependencies/factory-collaborators.adoc#beans-setter-injection[Setter-based dependency injection].
DI exists in two major variants: xref:core/beans/dependencies/factory-collaborators.adoc#beans-constructor-injection[Constructor-based dependency injection]
and xref:core/beans/dependencies/factory-collaborators.adoc#beans-setter-injection[Setter-based dependency injection].
[[beans-constructor-injection]]
@@ -35,7 +34,7 @@ injection:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
@@ -53,7 +52,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// a constructor so that the Spring container can inject a MovieFinder
class SimpleMovieLister(private val movieFinder: MovieFinder) {
@@ -78,7 +77,7 @@ being instantiated. Consider the following class:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package x.y;
@@ -92,7 +91,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package x.y
@@ -128,7 +127,7 @@ by type without help. Consider the following class:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package examples;
@@ -149,7 +148,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package examples
@@ -222,7 +221,7 @@ then have to look as follows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package examples;
@@ -240,7 +239,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package examples
@@ -266,7 +265,7 @@ on container specific interfaces, base classes, or annotations.
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
@@ -284,7 +283,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SimpleMovieLister {
@@ -441,7 +440,7 @@ The following example shows the corresponding `ExampleBean` class:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class ExampleBean {
@@ -467,7 +466,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class ExampleBean {
lateinit var beanOne: AnotherBean
@@ -504,7 +503,7 @@ The following example shows the corresponding `ExampleBean` class:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class ExampleBean {
@@ -525,7 +524,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class ExampleBean(
private val beanOne: AnotherBean,
@@ -558,7 +557,7 @@ The following example shows the corresponding `ExampleBean` class:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class ExampleBean {
@@ -582,7 +581,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class ExampleBean private constructor() {
companion object {
@@ -607,3 +606,6 @@ contains the `static` factory method (although, in this example, it is). An inst
(non-static) factory method can be used in an essentially identical fashion (aside
from the use of the `factory-bean` attribute instead of the `class` attribute), so we
do not discuss those details here.
@@ -3,7 +3,7 @@
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]
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
@@ -37,3 +37,6 @@ in the case of xref:core/beans/factory-scopes.adoc#beans-factory-scopes-singleto
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,22 +10,33 @@ 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.
This behavior is controlled by the `@Lazy` annotation or in XML the `lazy-init` attribute on the `<bean/>` element, as
the following example shows:
In XML, this behavior is controlled by the `lazy-init` attribute on the `<bean/>`
element, as the following example shows:
include-code::./ApplicationConfiguration[tag=snippet,indent=0]
[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"/>
----
When the preceding configuration is consumed by an `ApplicationContext`, the `lazy` bean
is not eagerly pre-instantiated when the `ApplicationContext` starts,
whereas the `notLazy` one is eagerly pre-instantiated.
whereas the `not.lazy` bean 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 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:
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:
[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]
@@ -2,17 +2,16 @@
= Method Injection
In most application scenarios, most beans in the container are
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-singleton[singletons]. When a
singleton bean needs to collaborate with another singleton bean or a non-singleton bean
needs to collaborate with another non-singleton bean, you typically handle the dependency
by defining one bean as a property of the other. A problem arises when the bean lifecycles
are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B,
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-singleton[singletons]. When a singleton bean needs to
collaborate with another singleton bean or a non-singleton bean needs to collaborate
with another non-singleton bean, you typically handle the dependency by defining one
bean as a property of the other. A problem arises when the bean lifecycles are
different. Suppose singleton bean A needs to use non-singleton (prototype) bean B,
perhaps on each method invocation on A. The container creates the singleton bean A only
once, and thus only gets one opportunity to set the properties. The container cannot
provide bean A with a new instance of bean B every time one is needed.
A solution is to forego some inversion of control. You can
xref:core/beans/factory-nature.adoc#beans-factory-aware[make bean A aware of the container]
A solution is to forego some inversion of control. You can xref:core/beans/factory-nature.adoc#beans-factory-aware[make bean A aware of the container]
by implementing the `ApplicationContextAware` interface,
and by xref:core/beans/basics.adoc#beans-factory-client[making a `getBean("B")` call to the container] ask for (a
typically new) bean B instance every time bean A needs it. The following example
@@ -22,7 +21,7 @@ shows this approach:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages",fold="none"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages",fold="none"]
----
package fiona.apple;
@@ -61,7 +60,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages",fold="none"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages",fold="none"]
----
package fiona.apple
@@ -104,6 +103,7 @@ You can read more about the motivation for Method Injection in
****
[[beans-factory-lookup-method-injection]]
== Lookup Method Injection
@@ -135,7 +135,7 @@ the reworked example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages",fold="none"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages",fold="none"]
----
package fiona.apple;
@@ -158,7 +158,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages",fold="none"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages",fold="none"]
----
package fiona.apple
@@ -218,7 +218,7 @@ method through the `@Lookup` annotation, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public abstract class CommandManager {
@@ -235,7 +235,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
abstract class CommandManager {
@@ -258,7 +258,7 @@ declared return type of the lookup method:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public abstract class CommandManager {
@@ -275,7 +275,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
abstract class CommandManager {
@@ -294,14 +294,14 @@ Kotlin::
[TIP]
====
Another way of accessing differently scoped target beans is an `ObjectFactory`/
`Provider` injection point. See
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other-injection[Scoped Beans as Dependencies].
`Provider` injection point. See xref:core/beans/factory-scopes.adoc#beans-factory-scopes-other-injection[Scoped Beans as Dependencies].
You may also find the `ServiceLocatorFactoryBean` (in the
`org.springframework.beans.factory.config` package) to be useful.
====
[[beans-factory-arbitrary-method-replacement]]
== Arbitrary Method Replacement
@@ -317,7 +317,7 @@ the following class, which has a method called `computeValue` that we want to ov
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MyValueCalculator {
@@ -331,7 +331,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MyValueCalculator {
@@ -351,7 +351,7 @@ interface provides the new method definition, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
/**
* meant to be used to override the existing computeValue(String)
@@ -370,7 +370,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
/**
* meant to be used to override the existing computeValue(String)
@@ -422,3 +422,6 @@ substring of the fully qualified type name. For example, the following all match
Because the number of arguments is often enough to distinguish between each possible
choice, this shortcut can save a lot of typing, by letting you type only the
shortest string that matches an argument type.
@@ -1,10 +1,11 @@
[[beans-factory-properties-detailed]]
= Dependencies and Configuration in Detail
As mentioned in the xref:core/beans/dependencies/factory-collaborators.adoc[previous section],
you can define bean properties and constructor arguments as references to other managed beans
(collaborators) or as values defined inline. Spring's XML-based configuration metadata supports
sub-element types within its `<property/>` and `<constructor-arg/>` elements for this purpose.
As mentioned in the xref:core/beans/dependencies/factory-collaborators.adoc[previous section], you can define bean
properties and constructor arguments as references to other managed beans (collaborators)
or as values defined inline. Spring's XML-based configuration metadata supports
sub-element types within its `<property/>` and `<constructor-arg/>` elements for this
purpose.
[[beans-value-element]]
@@ -50,8 +51,9 @@ XML configuration:
The preceding XML is more succinct. However, typos are discovered at runtime rather than
design time, unless you use an IDE (such as https://www.jetbrains.com/idea/[IntelliJ
IDEA] or the {spring-site-tools}[Spring Tools]) that supports automatic property
completion when you create bean definitions. Such IDE assistance is highly recommended.
IDEA] or the {spring-site-tools}[Spring Tools for Eclipse])
that supports automatic property completion when you create bean definitions. Such IDE
assistance is highly recommended.
You can also configure a `java.util.Properties` instance, as follows:
@@ -84,11 +86,11 @@ element. The following example shows how to use it:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="collaborator" class="..." />
<bean id="theTargetBean" class="..."/>
<bean id="client" class="...">
<bean id="theClientBean" class="...">
<property name="targetName">
<idref bean="collaborator" />
<idref bean="theTargetBean"/>
</property>
</bean>
----
@@ -98,24 +100,28 @@ following snippet:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="collaborator" class="..." />
<bean id="theTargetBean" class="..." />
<bean id="client" class="...">
<property name="targetName" value="collaborator" />
<bean id="theClientBean" class="...">
<property name="targetName" ref="theTargetBean"/>
</bean>
----
The first form is preferable to the second, because using the `idref` tag lets the
container validate at deployment time that the referenced, named bean actually exists. In
the second variation, no validation is performed on the value that is passed to the
`targetName` property of the `client` bean. Typos are therefore only discovered (with most
container validate at deployment time that the referenced, named bean actually
exists. In the second variation, no validation is performed on the value that is passed
to the `targetName` property of the `client` bean. Typos are only discovered (with most
likely fatal results) when the `client` bean is actually instantiated. If the `client`
bean is a xref:core/beans/factory-scopes.adoc[prototype] bean, this typo and the resulting
exception may only be discovered long after the container is deployed.
bean is a xref:core/beans/factory-scopes.adoc[prototype] bean, this typo and the resulting exception
may only be discovered long after the container is deployed.
NOTE: A common place (at least in versions earlier than Spring 2.0) where the `<idref/>`
element brings value is in the configuration of xref:core/aop-api/pfb.adoc#aop-pfb-1[AOP interceptors]
in a `ProxyFactoryBean` bean definition. Using `<idref/>` elements when you specify the
NOTE: The `local` attribute on the `idref` element is no longer supported in the 4.0 beans
XSD, since it does not provide value over a regular `bean` reference any more. Change
your existing `idref local` references to `idref bean` when upgrading to the 4.0 schema.
A common place (at least in versions earlier than Spring 2.0) where the `<idref/>` element
brings value is in the configuration of xref:core/aop-api/pfb.adoc#aop-pfb-1[AOP interceptors] in a
`ProxyFactoryBean` bean definition. Using `<idref/>` elements when you specify the
interceptor names prevents you from misspelling an interceptor ID.
@@ -162,8 +168,8 @@ listings shows how to use the `parent` attribute:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- in the child (descendant) context, bean name is the same as the parent bean -->
<bean id="accountService"
<!-- in the child (descendant) context -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
@@ -348,7 +354,7 @@ The following Java class and bean definition show how to do so:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SomeClass {
@@ -362,7 +368,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SomeClass {
lateinit var accounts: Map<String, Float>
@@ -412,14 +418,14 @@ The preceding example is equivalent to the following Java code:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
exampleBean.setEmail("");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
exampleBean.email = ""
----
@@ -443,14 +449,14 @@ The preceding configuration is equivalent to the following Java code:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
exampleBean.setEmail(null);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
exampleBean.email = null
----
@@ -538,10 +544,9 @@ three approaches at the same time.
[[beans-c-namespace]]
== XML Shortcut with the c-namespace
Similar to the
xref:core/beans/dependencies/factory-properties-detailed.adoc#beans-p-namespace[XML Shortcut with the p-namespace],
the c-namespace, introduced in Spring 3.1, allows inlined attributes for configuring
the constructor arguments rather then nested `constructor-arg` elements.
Similar to the xref:core/beans/dependencies/factory-properties-detailed.adoc#beans-p-namespace[XML Shortcut with the p-namespace], the c-namespace, introduced in Spring
3.1, allows inlined attributes for configuring the constructor arguments rather
then nested `constructor-arg` elements.
The following example uses the `c:` namespace to do the same thing as the from
xref:core/beans/dependencies/factory-collaborators.adoc#beans-constructor-injection[Constructor-based Dependency Injection]:
@@ -593,9 +598,9 @@ A corresponding index notation is also available for `<constructor-arg>` element
not commonly used since the plain order of declaration is usually sufficient there.
In practice, the constructor resolution
xref:core/beans/dependencies/factory-collaborators.adoc#beans-factory-ctor-arguments-resolution[mechanism]
is quite efficient in matching arguments, so unless you really need to, we recommend
using the name notation throughout your configuration.
xref:core/beans/dependencies/factory-collaborators.adoc#beans-factory-ctor-arguments-resolution[mechanism] is quite efficient in matching
arguments, so unless you really need to, we recommend using the name notation
throughout your configuration.
[[beans-compound-property-names]]
@@ -616,3 +621,6 @@ The `something` bean has a `fred` property, which has a `bob` property, which ha
property, and that final `sammy` property is being set to a value of `123`. In order for
this to work, the `fred` property of `something` and the `bob` property of `fred` must not
be `null` after the bean is constructed. Otherwise, a `NullPointerException` is thrown.
@@ -20,6 +20,7 @@ user with a convenient service interface for configuring property sources and re
properties from them.
[[beans-definition-profiles]]
== Bean Definition Profiles
@@ -42,7 +43,7 @@ Consider the first use case in a practical application that requires a
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Bean
public DataSource dataSource() {
@@ -56,7 +57,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Bean
fun dataSource(): DataSource {
@@ -78,7 +79,7 @@ now looks like the following listing:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Bean(destroyMethod = "")
public DataSource dataSource() throws Exception {
@@ -89,7 +90,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Bean(destroyMethod = "")
fun dataSource(): DataSource {
@@ -113,6 +114,7 @@ certain contexts but not in others. You could say that you want to register a
certain profile of bean definitions in situation A and a different profile in
situation B. We start by updating our configuration to reflect this need.
[[beans-definition-profiles-java]]
=== Using `@Profile`
@@ -126,7 +128,7 @@ can rewrite the `dataSource` configuration as follows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@Profile("development")
@@ -145,7 +147,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@Profile("development")
@@ -169,7 +171,7 @@ Kotlin::
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@Profile("production")
@@ -186,7 +188,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@Profile("production")
@@ -231,7 +233,7 @@ of creating a custom composed annotation. The following example defines a custom
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@@ -242,7 +244,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@@ -270,7 +272,7 @@ the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class AppConfig {
@@ -298,7 +300,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class AppConfig {
@@ -343,6 +345,7 @@ way to represent such an arrangement in a valid Java class in the first place
(since there can only be one method of a particular name and argument signature).
====
[[beans-definition-profiles-xml]]
=== XML Bean Definition Profiles
@@ -434,6 +437,7 @@ In the preceding example, the `dataSource` bean is exposed if both the `producti
`us-east` profiles are active.
=====
[[beans-definition-profiles-enable]]
=== Activating a Profile
@@ -450,7 +454,7 @@ it programmatically against the `Environment` API which is available through an
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
@@ -460,7 +464,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val ctx = AnnotationConfigApplicationContext().apply {
environment.setActiveProfiles("development")
@@ -487,14 +491,14 @@ activates multiple profiles:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
ctx.getEnvironment().setActiveProfiles("profile1", "profile2")
----
@@ -508,6 +512,7 @@ as the following example shows:
-Dspring.profiles.active="profile1,profile2"
----
[[beans-definition-profiles-default]]
=== Default Profile
@@ -518,7 +523,7 @@ the following example:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@Profile("default")
@@ -536,7 +541,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@Profile("default")
@@ -562,6 +567,7 @@ the default profile by using `setDefaultProfiles()` on the `Environment` or,
declaratively, by using the `spring.profiles.default` property.
[[beans-property-source-abstraction]]
== `PropertySource` Abstraction
@@ -572,7 +578,7 @@ hierarchy of property sources. Consider the following listing:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
@@ -582,7 +588,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val ctx = GenericApplicationContext()
val env = ctx.environment
@@ -637,7 +643,7 @@ current `Environment`. The following example shows how to do so:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
@@ -646,7 +652,7 @@ sources.addFirst(new MyPropertySource());
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val ctx = GenericApplicationContext()
val sources = ctx.environment.propertySources
@@ -662,6 +668,7 @@ API exposes a number of methods that allow for precise manipulation of the set o
property sources.
[[beans-using-propertysource]]
== Using `@PropertySource`
@@ -677,7 +684,7 @@ a call to `testBean.getName()` returns `myTestBean`:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
@@ -697,7 +704,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
@@ -722,7 +729,7 @@ environment, as the following example shows:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
@@ -742,7 +749,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@PropertySource("classpath:/com/\${my.placeholder:default/path}/app.properties")
@@ -770,6 +777,7 @@ may also be used as a meta-annotation to create custom composed annotations with
attribute overrides.
[[beans-placeholder-resolution-in-statements]]
== Placeholder Resolution in Statements
@@ -790,3 +798,7 @@ property is defined, as long as it is available in the `Environment`:
<import resource="com/bank/service/${customer}-config.xml"/>
</beans>
----
@@ -7,6 +7,7 @@ implementations of special integration interfaces. The next few sections describ
integration interfaces.
[[beans-factory-extension-bpp]]
== Customizing Beans by Using a `BeanPostProcessor`
@@ -108,6 +109,7 @@ Spring accesses other beans for matching them by type.
The following examples show how to write, register, and use `BeanPostProcessor` instances
in an `ApplicationContext`.
[[beans-factory-extension-bpp-examples-hw]]
=== Example: Hello World, `BeanPostProcessor`-style
@@ -121,7 +123,7 @@ The following listing shows the custom `BeanPostProcessor` implementation class
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package scripting;
@@ -143,7 +145,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",chomp="-packages"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package scripting
@@ -203,7 +205,7 @@ The following Java application runs the preceding code and configuration:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@@ -222,9 +224,9 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
import org.springframework.beans.factory.getBean
import org.springframework.beans.factory.getBean
fun main() {
val ctx = ClassPathXmlApplicationContext("scripting/beans.xml")
@@ -242,6 +244,7 @@ Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@
org.springframework.scripting.groovy.GroovyMessenger@272961
----
[[beans-factory-extension-bpp-examples-aabpp]]
=== Example: The `AutowiredAnnotationBeanPostProcessor`
@@ -252,6 +255,7 @@ that ships with the Spring distribution and autowires annotated fields, setter m
and arbitrary config methods.
[[beans-factory-extension-factory-postprocessors]]
== Customizing Configuration Metadata with a `BeanFactoryPostProcessor`
@@ -308,8 +312,9 @@ Thus, marking it for lazy initialization will be ignored, and the
`Bean(Factory)PostProcessor` will be instantiated eagerly even if you set the
`default-lazy-init` attribute to `true` on the declaration of your `<beans />` element.
[[beans-factory-placeholderconfigurer]]
=== Example: Property Placeholder Substitution with `PropertySourcesPlaceholderConfigurer`
=== Example: The Class Name Substitution `PropertySourcesPlaceholderConfigurer`
You can use the `PropertySourcesPlaceholderConfigurer` to externalize property values
from a bean definition in a separate file by using the standard Java `Properties` format.
@@ -336,8 +341,8 @@ with placeholder values is defined:
The example shows properties configured from an external `Properties` file. At runtime,
a `PropertySourcesPlaceholderConfigurer` is applied to the metadata that replaces some
properties of the `DataSource`. The values to replace are specified as placeholders of the
form pass:q[`${property-name}`], which follows the Ant, log4j, and JSP EL style.
properties of the DataSource. The values to replace are specified as placeholders of the
form pass:q[`${property-name}`], which follows the Ant and log4j and JSP EL style.
The actual values come from another file in the standard Java `Properties` format:
@@ -350,15 +355,11 @@ jdbc.password=root
----
Therefore, the `${jdbc.username}` string is replaced at runtime with the value, 'sa', and
the same applies for other placeholder values that match keys in the properties file. The
`PropertySourcesPlaceholderConfigurer` checks for placeholders in most properties and
attributes of a bean definition. Furthermore, you can customize the placeholder prefix,
suffix, default value separator, and escape character. In addition, the default escape
character can be changed or disabled globally by setting the
`spring.placeholder.escapeCharacter.default` property via a JVM system property (or via
the xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism).
the same applies for other placeholder values that match keys in the properties file.
The `PropertySourcesPlaceholderConfigurer` checks for placeholders in most properties and
attributes of a bean definition. Furthermore, you can customize the placeholder prefix and suffix.
With the `context` namespace, you can configure property placeholders
With the `context` namespace introduced in Spring 2.5, you can configure property placeholders
with a dedicated configuration element. You can provide one or more locations as a
comma-separated list in the `location` attribute, as the following example shows:
@@ -407,6 +408,7 @@ fails when it is about to be created, which is during the `preInstantiateSinglet
phase of an `ApplicationContext` for a non-lazy-init bean.
=====
[[beans-factory-overrideconfigurer]]
=== Example: The `PropertyOverrideConfigurer`
@@ -463,6 +465,7 @@ property overriding with a dedicated configuration element, as the following exa
----
[[beans-factory-extension-factorybean]]
== Customizing Instantiation Logic with a `FactoryBean`
@@ -495,3 +498,6 @@ calling the `getBean()` method of the `ApplicationContext`. So, for a given `Fac
with an `id` of `myBean`, invoking `getBean("myBean")` on the container returns the
product of the `FactoryBean`, whereas invoking `getBean("&myBean")` returns the
`FactoryBean` instance itself.
@@ -9,6 +9,7 @@ of a bean. This section groups them as follows:
* xref:core/beans/factory-nature.adoc#aware-list[Other `Aware` Interfaces]
[[beans-factory-lifecycle]]
== Lifecycle Callbacks
@@ -40,6 +41,8 @@ startup and shutdown process, as driven by the container's own lifecycle.
The lifecycle callback interfaces are described in this section.
[[beans-factory-lifecycle-initializingbean]]
=== Initialization Callbacks
@@ -69,7 +72,7 @@ no-argument signature. With Java configuration, you can use the `initMethod` att
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class ExampleBean {
@@ -81,7 +84,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class ExampleBean {
@@ -104,7 +107,7 @@ The preceding example has almost exactly the same effect as the following exampl
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class AnotherExampleBean implements InitializingBean {
@@ -117,7 +120,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class AnotherExampleBean : InitializingBean {
@@ -141,7 +144,7 @@ based on the given configuration but no further activity with external bean acce
Otherwise there is a risk for an initialization deadlock.
For a scenario where expensive post-initialization activity is to be triggered,
for example, asynchronous database preparation steps, your bean should either implement
e.g. asynchronous database preparation steps, your bean should either implement
`SmartInitializingSingleton.afterSingletonsInstantiated()` or rely on the context
refresh event: implementing `ApplicationListener<ContextRefreshedEvent>` or
declaring its annotation equivalent `@EventListener(ContextRefreshedEvent.class)`.
@@ -153,6 +156,8 @@ the container's overall lifecycle management, including an auto-startup mechanis
a pre-destroy stop step, and potential stop/restart callbacks (see below).
====
[[beans-factory-lifecycle-disposablebean]]
=== Destruction Callbacks
@@ -182,7 +187,7 @@ xref:core/beans/java/bean-annotation.adoc#beans-java-lifecycle-callbacks[Receivi
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class ExampleBean {
@@ -194,7 +199,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class ExampleBean {
@@ -216,7 +221,7 @@ The preceding definition has almost exactly the same effect as the following def
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class AnotherExampleBean implements DisposableBean {
@@ -229,7 +234,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class AnotherExampleBean : DisposableBean {
@@ -262,6 +267,8 @@ You may also implement `SmartLifecycle` for a time-bound stop step where the con
will wait for all such stop processing to complete before moving on to destroy methods.
====
[[beans-factory-lifecycle-default-init-destroy-methods]]
=== Default Initialization and Destroy Methods
@@ -288,7 +295,7 @@ following example:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class DefaultBlogService implements BlogService {
@@ -309,7 +316,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class DefaultBlogService : BlogService {
@@ -362,6 +369,8 @@ interceptors to the `init` method, because doing so would couple the lifecycle o
target bean to its proxy or interceptors and leave strange semantics when your code
interacts directly with the raw target bean.
[[beans-factory-lifecycle-combined-effects]]
=== Combining Lifecycle Mechanisms
@@ -393,6 +402,8 @@ Destroy methods are called in the same order:
. `destroy()` as defined by the `DisposableBean` callback interface
. A custom configured `destroy()` method
[[beans-factory-lifecycle-processor]]
=== Startup and Shutdown Callbacks
@@ -515,6 +526,8 @@ its own `start()` method (unlike the context refresh, the context start does not
automatically for a standard context implementation). The `phase` value and any
"`depends-on`" relationships determine the startup order as described earlier.
[[beans-factory-shutdown]]
=== Shutting Down the Spring IoC Container Gracefully in Non-Web Applications
@@ -538,7 +551,7 @@ declared on the `ConfigurableApplicationContext` interface, as the following exa
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@@ -560,7 +573,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
import org.springframework.context.support.ClassPathXmlApplicationContext
@@ -577,6 +590,8 @@ Kotlin::
----
======
[[beans-factory-thread-safety]]
=== Thread Safety and Visibility
@@ -592,7 +607,7 @@ bean creation phase and its subsequent initial publication, they need to be decl
`volatile` or guarded by a common lock whenever accessed.
Note that concurrent access to such configuration state in singleton bean instances,
for example, for controller instances or repository instances, is perfectly thread-safe after
e.g. for controller instances or repository instances, is perfectly thread-safe after
such safe initial publication from the container side. This includes common singleton
`FactoryBean` instances which are processed within the general singleton lock as well.
@@ -602,7 +617,7 @@ structures (or in `volatile` fields for simple cases) as per common Java guideli
Deeper `Lifecycle` integration as shown above involves runtime-mutable state such as
a `runnable` field which will have to be declared as `volatile`. While the common
lifecycle callbacks follow a certain order, for example, a start callback is guaranteed to
lifecycle callbacks follow a certain order, e.g. a start callback is guaranteed to
only happen after full initialization and a stop callback only after an initial start,
there is a special case with the common stop before destroy arrangement: It is strongly
recommended that the internal state in any such bean also allows for an immediate
@@ -610,6 +625,7 @@ destroy callback without a preceding stop since this may happen during an extrao
shutdown after a cancelled bootstrap or in case of a stop timeout caused by another bean.
[[beans-factory-aware]]
== `ApplicationContextAware` and `BeanNameAware`
@@ -666,6 +682,7 @@ initialization callback such as `InitializingBean.afterPropertiesSet()` or a cus
init-method.
[[aware-list]]
== Other `Aware` Interfaces
@@ -730,3 +747,6 @@ dependency type. The following table summarizes the most important `Aware` inter
Note again that using these interfaces ties your code to the Spring API and does not
follow the Inversion of Control style. As a result, we recommend them for infrastructure
beans that require programmatic access to the container.
@@ -56,6 +56,7 @@ For instructions on how to register this or any other custom scope, see
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-custom-using[Using a Custom Scope].
[[beans-factory-scopes-singleton]]
== The Singleton Scope
@@ -90,6 +91,7 @@ following example:
----
[[beans-factory-scopes-prototype]]
== The Prototype Scope
@@ -132,6 +134,7 @@ be handled by the client. (For details on the lifecycle of a bean in the Spring
container, see xref:core/beans/factory-nature.adoc#beans-factory-lifecycle[Lifecycle Callbacks].)
[[beans-factory-scopes-sing-prot-interaction]]
== Singleton Beans with Prototype-bean Dependencies
@@ -149,6 +152,7 @@ and injects its dependencies. If you need a new instance of a prototype bean at
runtime more than once, see xref:core/beans/dependencies/factory-method-injection.adoc[Method Injection].
[[beans-factory-scopes-other]]
== Request, Session, Application, and WebSocket Scopes
@@ -158,6 +162,8 @@ if you use a web-aware Spring `ApplicationContext` implementation (such as
such as the `ClassPathXmlApplicationContext`, an `IllegalStateException` that complains
about an unknown bean scope is thrown.
[[beans-factory-scopes-other-web-configuration]]
=== Initial Web Configuration
@@ -217,6 +223,8 @@ the same thing, namely bind the HTTP request object to the `Thread` that is serv
that request. This makes beans that are request- and session-scoped available further
down the call chain.
[[beans-factory-scopes-request]]
=== Request scope
@@ -243,7 +251,7 @@ to do so:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@RequestScope
@Component
@@ -254,7 +262,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@RequestScope
@Component
@@ -264,6 +272,8 @@ Kotlin::
----
======
[[beans-factory-scopes-session]]
=== Session Scope
@@ -291,7 +301,7 @@ When using annotation-driven components or Java configuration, you can use the
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@SessionScope
@Component
@@ -302,7 +312,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@SessionScope
@Component
@@ -312,6 +322,8 @@ Kotlin::
----
======
[[beans-factory-scopes-application]]
=== Application Scope
@@ -338,7 +350,7 @@ following example shows how to do so:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@ApplicationScope
@Component
@@ -349,7 +361,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@ApplicationScope
@Component
@@ -359,6 +371,8 @@ Kotlin::
----
======
[[beans-factory-scopes-websocket]]
=== WebSocket Scope
@@ -366,6 +380,8 @@ WebSocket scope is associated with the lifecycle of a WebSocket session and appl
STOMP over WebSocket applications, see
xref:web/websocket/stomp/scope.adoc[WebSocket scope] for more details.
[[beans-factory-scopes-other-injection]]
=== Scoped Beans as Dependencies
@@ -523,6 +539,8 @@ interfaces. The following example shows a proxy based on an interface:
For more detailed information about choosing class-based or interface-based proxying,
see xref:core/aop/proxying.adoc[Proxying Mechanisms].
[[beans-factory-scopes-injection]]
=== Injecting Request/Session References Directly
@@ -535,6 +553,7 @@ objects which has the advantage of working in singleton beans and serializable b
as well, similar to scoped proxies for factory-scoped beans.
[[beans-factory-scopes-custom]]
== Custom Scopes
@@ -542,6 +561,7 @@ The bean scoping mechanism is extensible. You can define your own
scopes or even redefine existing scopes, although the latter is considered bad practice
and you cannot override the built-in `singleton` and `prototype` scopes.
[[beans-factory-scopes-custom-creating]]
=== Creating a Custom Scope
@@ -564,14 +584,14 @@ underlying scope:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Object get(String name, ObjectFactory<?> objectFactory)
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
fun get(name: String, objectFactory: ObjectFactory<*>): Any
----
@@ -586,14 +606,14 @@ the underlying scope:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Object remove(String name)
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
fun remove(name: String): Any
----
@@ -606,14 +626,14 @@ destroyed or when the specified object in the scope is destroyed:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
void registerDestructionCallback(String name, Runnable destructionCallback)
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
fun registerDestructionCallback(name: String, destructionCallback: Runnable)
----
@@ -628,14 +648,14 @@ The following method obtains the conversation identifier for the underlying scop
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
String getConversationId()
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
fun getConversationId(): String
----
@@ -644,6 +664,8 @@ Kotlin::
This identifier is different for each scope. For a session scoped implementation, this
identifier can be the session identifier.
[[beans-factory-scopes-custom-using]]
=== Using a Custom Scope
@@ -655,14 +677,14 @@ method to register a new `Scope` with the Spring container:
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
void registerScope(String scopeName, Scope scope);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
fun registerScope(scopeName: String, scope: Scope)
----
@@ -688,7 +710,7 @@ implementations.
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);
@@ -696,7 +718,7 @@ Java::
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val threadScope = SimpleThreadScope()
beanFactory.registerScope("thread", threadScope)
@@ -751,3 +773,7 @@ of the scope. You can also do the `Scope` registration declaratively, by using t
NOTE: When you place `<aop:scoped-proxy/>` within a `<bean>` declaration for a
`FactoryBean` implementation, it is the factory bean itself that is scoped, not the object
returned from `getObject()`.

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