Compare commits

...

992 Commits

Author SHA1 Message Date
Spring Builds 8404537474 Release v6.0.11 2023-07-13 08:28:02 +00:00
Juergen Hoeller c873a597c7 Polishing 2023-07-12 19:21:44 +02:00
Stephane Nicoll 4dc93bc485 Avoid ambiguous call with BeanInstanceSupplier#withGenerator
Previously, BeanInstanceSupplier had three variants of the
`withGenerator` callback, one with a bi function, one with a function,
and with a supplier. This could lead to compilation failure when the
target type has a method with the same name and a number of arguments
that match another variant.

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

Closes gh-29278
2023-07-12 17:23:08 +02:00
Sam Brannen 68f2b0ca59 Rely on auto-boxing in tests 2023-07-12 11:49:02 +02:00
Sam Brannen 1edc0d8002 Remove outdated Javadoc cross references 2023-07-12 10:36:02 +02:00
Sam Brannen a6dc020dc4 Remove since tag 2023-07-12 10:33:24 +02:00
Sam Brannen 07422d709e Remove getAutodetectMode() in MBeanExporter
After further consideration, the team has decided to remove the
getAutodetectMode() method since its return type conflicts with the
parameter type in setAutodetectMode(int), making it an invalid bean
property.

See gh-30855
2023-07-12 10:30:38 +02:00
Brian Clozel 768fc7e341 Upgrade to Reactor 2022.0.9
Closes gh-30871
2023-07-12 08:41:46 +02:00
Johnny Lim 8ecedb81b3 Add missing @Nullable annotations in ContentDisposition.Builder
Closes gh-30820
2023-07-11 19:58:29 +02:00
Juergen Hoeller f19433f2d8 Polishing 2023-07-11 18:01:07 +02:00
Juergen Hoeller 0b02a5e073 Avoid illegal reflective access in ContextOverridingClassLoader
Closes gh-22791
2023-07-11 17:51:55 +02:00
Juergen Hoeller e2b24f3c12 Improve diagnostics for LinkageError in case of ClassLoader mismatch
Closes gh-25940
2023-07-11 17:50:44 +02:00
Juergen Hoeller c375fb1f70 Deprecate setAllowResultAccessAfterCompletion and document it as broken
Closes gh-26557
2023-07-11 17:50:31 +02:00
Sam Brannen ad3e5425d4 Reduce WARN level log output during test AOT processing
Closes gh-30867
2023-07-11 16:35:07 +02:00
Sam Brannen c418118683 Polishing 2023-07-11 15:37:55 +02:00
Sam Brannen fb3f30832c Delete obsolete constant in AnnotationConfigUtils
See gh-30695
2023-07-11 15:33:28 +02:00
Sébastien Deleuze a275d942d2 Update STOMP documentation with the new guidelines
This commit changes the documentation to advise using
https://github.com/stomp-js/stompjs library on client
side and to configure by default WebSocket without
SockJS as browsers and proxies now have a pretty good
WebSocket support.

Closes gh-30857
2023-07-11 14:07:15 +02:00
rstoyanchev 20afa3265a Encapsulate full path initialization 2023-07-11 11:10:20 +01:00
Sam Brannen df50c8db3e Upgrade to micrometer-bom 1.10.9 and context-propagation 1.0.4
Closes gh-30860
2023-07-11 10:58:11 +02:00
Sam Brannen 679b668bbb Avoid need for reflection hints for MBeanExporter in native image
Prior to this commit, MBeanExporter used
org.springframework.core.Constants which used reflection to find
constant fields in the MBeanExporter class. Consequently, one had to
register reflection hints in order to use MBeanExporter in a GraalVM
native image.

This commit addresses this by replacing the use of the `Constants`
class with a simple java.util.Map which maps constant names to constant
values for the autodetect constants defined in MBeanExporter.

See gh-30851
Closes gh-30846
2023-07-10 19:01:44 +02:00
Sam Brannen 676daa990b Reorganize methods in MBeanExporter 2023-07-10 18:28:45 +02:00
Sam Brannen 7c7fa69558 Introduce getAutodetectMode() in MBeanExporter
Closes gh-30855
2023-07-10 18:26:33 +02:00
Sam Brannen 9ad92b16b0 Polish MBeanExporterTests 2023-07-10 18:20:39 +02:00
Juergen Hoeller 3b899fe7e2 Handle JDBC warnings in case of a statement exception as well
Closes gh-23106
2023-07-10 17:17:30 +02:00
Sébastien Deleuze c91041b675 Improve RSocketClientToServer*IntegrationTests reliability
Those new delays seems to make tests more reliable.

Closes gh-30844
2023-07-09 18:39:50 +02:00
Juergen Hoeller a17cf742b2 Polishing 2023-07-09 16:55:21 +02:00
Juergen Hoeller a102cd5f32 Tolerate isCandidateClass call with null as annotation type
Closes gh-30842
2023-07-09 16:52:54 +02:00
Juergen Hoeller d03b6aa1d6 Reinstate support for legacy JSR-250 Resource annotation
This merges the existing support for the legacy JSR-250 PostConstruct/PreDestroy annotations into CommonAnnotationBeanPostProcessor itself, opening up the InitDestroyAnnotationBeanPostProcessor base class for multiple init/destroy methods in a single post-processor. This removes the need for a separate JSR-250 InitDestroyAnnotationBeanPostProcessor in AnnotationConfigUtils.

Closes gh-30695
2023-07-09 16:52:17 +02:00
Sam Brannen b32b4f3a59 Polish DefaultSingletonBeanRegistryTests 2023-07-09 15:32:20 +02:00
Sam Brannen 502997d8e9 Further simplify DefaultSingletonBeanRegistry.isDependent()
See gh-30839
2023-07-09 15:32:20 +02:00
bnbakp0582 fb4ad2f3ba Simplify DefaultSingletonBeanRegistry.isDependent()
Move `alreadySeen` handling out of for-loop.

Closes gh-30839
2023-07-09 15:22:35 +02:00
Sébastien Deleuze b3de1b8e95 Use consistently *KotlinTests naming for Kotlin tests
Closes gh-30837
2023-07-08 11:02:20 +02:00
Sébastien Deleuze fb17e283d1 Replace @link by proper KDoc class reference in tests
Closes gh-30836
2023-07-08 10:44:40 +02:00
Juergen Hoeller 0b7a24fc14 Polishing 2023-07-08 00:58:20 +02:00
Sam Brannen 75b540f25c Update copyright headers 2023-07-07 15:03:09 +02:00
Sam Brannen 8bf79cc9c4 Polish contribution
Closes gh-30593
2023-07-07 15:02:38 +02:00
Heo YounHaeng 7ff80bc09d Fix example in Javadoc for MultipartBodyBuilder
See gh-30593
2023-07-07 15:00:47 +02:00
Valery Yatsynovich 8d6b0eb191 Fix typo in UriUtils Javadoc
Closes gh-30598
2023-07-07 14:50:46 +02:00
Juergen Hoeller 35c7e3960e Polishing 2023-07-07 13:46:57 +02:00
Juergen Hoeller 8e8c3f5a7c Polishing 2023-07-07 13:14:40 +02:00
Sam Brannen f50b230fb3 Emphasize that a ContextCustomizer must implement equals() and hashCode() 2023-07-06 17:28:24 +02:00
Sam Brannen 02ba06953f Polish grammar and formatting 2023-07-06 17:21:30 +02:00
Brian Clozel df22ba39f8 Warn against direct usage of Servlet API in WebFlux apps
Closes gh-28872
2023-07-06 17:11:24 +02:00
Sam Brannen 826776f321 Improve assertions in DefaultConversionServiceTests
Specifically, we now check the actual type of a converted collection in
various assertions to ensure that converters adhere to their contracts.
2023-07-06 14:01:26 +02:00
Sam Brannen 29f92c8a2b Polish observability docs 2023-07-06 13:27:20 +02:00
Brian Clozel 64e04b7bc2 Document limitations of Servlet Filter observations
This commit documents the fact that the Servlet Filter based
observations for MVC applications is limited by the Servlet Filter
contract in the first place. All processing and logging that happens
outside of the scope of the filter is not observed.
Log statements from the catalina engine (in the case of Tomcat), or any
container-specific infrastructure, is not covered by the
instrumentation.

Closes gh-29398
2023-07-06 13:09:41 +02:00
Sam Brannen ad05b02ff5 Update Javadoc for ObjectUtils.nullSafeConciseToString()
See gh-30810
2023-07-06 12:26:47 +02:00
Brian Clozel 430a24e6bc Further document ShallowEtagHeaderFilter limitations
This commit improves the documentation for the
`ShallowEtagHeaderFilter`, stating that it is only meant to support a
subset of conditional HTTP requests: GET requests with "If-None-Match"
headers. Other headers and state changing HTTP methods are not supported
here, as the filter only operates on the content of the response and has
no knowledge of the resource being served.

Closes gh-30517
2023-07-06 09:39:57 +02:00
Juergen Hoeller b7b9f2cb6b Expand tests for array to Collection/Set/List interface
See gh-28048
2023-07-05 20:15:10 +02:00
Sam Brannen b76664e757 Support arrays, collections, & maps in ObjectUtils.nullSafeConciseToString()
Prior to this commit, there was no explicit support for arrays,
collections, and maps in nullSafeConciseToString(). This lead to string
representations such as the following, regardless of whether the array,
collection, or map was empty.

- char[]@1623b78d
- java.util.ImmutableCollections$List12@74fe5c40
- java.util.ImmutableCollections$MapN@10e31a9a

This commit introduces explicit support for arrays, collections, and
maps in nullSafeConciseToString(), which results in the following
empty/non-empty string representations.

- array: {} / {...}
- collection: [] / [...]
- map: {} / {...}

The reason a string representation of an array uses "{}" instead of
"[]" (like in Arrays.toString(...)) is that
ObjectUtils.nullSafeToString(<array>) already follows that convention,
and the implementation of nullSafeConciseToString() aligns with that
for the sake of consistency.

Closes gh-30810
2023-07-05 17:01:01 +02:00
Sam Brannen ae13823851 Polishing 2023-07-05 15:33:15 +02:00
Sam Brannen 58b4286216 Polish MockHttpServletRequest 2023-07-05 13:59:47 +02:00
Sam Brannen df079feea9 Update copyright header 2023-07-05 13:48:13 +02:00
Patrick Strawderman 372282457f Use Collections.emptyEnumeration() where appropriate
Closes gh-30803
2023-07-05 13:47:11 +02:00
Juergen Hoeller 79df1da792 Clarify ReactiveTransactionManager exception declarations
Avoid misleading "throws TransactionException" declarations but preserve javadoc "@throws" notes for specific exceptions (with reactive propagation semantics).

Closes gh-30817
2023-07-05 12:51:45 +02:00
Juergen Hoeller c5771bc7c8 Discuss JdbcTransactionManager vs DataSourceTransactionManager
Includes doc update for 6.0 default exception translation etc.

Closes gh-30802
2023-07-05 12:51:10 +02:00
Juergen Hoeller 2365581265 Upgrade to HSQLDB 2.7.2 and WebJars Locator 0.53 2023-07-04 21:35:37 +02:00
Juergen Hoeller c1a8b9a14d Polishing 2023-07-04 21:24:35 +02:00
rstoyanchev e945e7426e Update AspectJ Javadoc link after permanent redirect 2023-07-04 18:09:55 +01:00
Juergen Hoeller f07b9fd217 Polishing 2023-07-04 16:52:44 +02:00
Juergen Hoeller 80a20488fd Make File/Path tests pass on Windows
See gh-30806
2023-07-04 16:52:39 +02:00
Juergen Hoeller 1dc9dffc70 Restore full representation of rejected value in FieldError.toString()
We would preferably use ObjectUtils.nullSafeConciseToString(rejectedValue) here but revert to the full nullSafeToString representation for strict backwards compatibility (programmatic toString calls as well as exception messages).

Closes gh-30799
2023-07-04 15:58:46 +02:00
Juergen Hoeller 0226580773 Refresh cached value after unexpected mismatch (e.g. null vs non-null)
In addition to the previously addressed removal of bean definitions, this is able to deal with prototype factory methods returning non-null after null or also null after non-null. Stale cached values are getting refreshed rather than bypassed.

Closes gh-30794
2023-07-04 15:58:30 +02:00
Sam Brannen 3ef1b7d83c Extend supported types in ObjectUtils.nullSafeConciseToString()
This commit extends the list of explicitly supported types in
ObjectUtils.nullSafeConciseToString() with the following.

- Optional
- File
- Path
- InetAddress
- Charset
- Currency
- TimeZone
- ZoneId
- Pattern

Closes gh-30805
2023-07-04 13:27:47 +02:00
Sam Brannen 08bce69d3d Add tests for status quo in ObjectUtils.nullSafeConciseToString() 2023-07-04 13:15:56 +02:00
Yanming Zhou 56b60120fe Stop using deprecated AbstractArchiveTask.archivePath in Gradle build
This commit addresses the following warning in the build.

The AbstractArchiveTask.archivePath property has been deprecated. This
is scheduled to be removed in Gradle 9.0. Please use the archiveFile
property instead. See
https://docs.gradle.org/8.1.1/dsl/org.gradle.api.tasks.bundling.AbstractArchiveTask.html#org.gradle.api.tasks.bundling.AbstractArchiveTask:archivePath
for more details.

Closes gh-30697
2023-07-03 17:09:48 +02:00
Sam Brannen 1364a179a9 Upgrade to Gradle 8.2
Closes gh-30791
2023-07-03 16:24:34 +02:00
Sam Brannen 2161e865d7 Update copyright header 2023-07-03 15:53:43 +02:00
Vladyslav Baidak 0b2c2d04b2 Fix typo in Javadoc for BeanDefinitionDsl.kt
Closes gh-30798
2023-07-03 15:49:28 +02:00
Sam Brannen 07fe8eea83 Improve error message for wrong version of micrometer-observation
This commit also reverts to using ReflectionUtils.findMethod in order
to make the check more robust in case the Micrometer team refactors the
code base and declares the `getObservationRegistry()` method in a super
type.
2023-07-03 14:48:53 +02:00
Sam Brannen 5aac35b99e Check deps only once in MicrometerObservationRegistryTestExecutionListener
Prior to this commit, the required runtime dependencies were checked
via reflection each time an attempt was made to instantiate
MicrometerObservationRegistryTestExecutionListener.

Since it's sufficient to check for the presence of required runtime
dependencies only once, this commit caches the results of the
dependency checks in a static field.

This commit also introduces automated tests for the runtime dependency
checks in MicrometerObservationRegistryTestExecutionListener.

See gh-30747
2023-07-02 16:40:41 +02:00
Sam Brannen 040ea0a97c Remove @Aspect for classes containing only @Pointcut declarations in ref docs
Closes gh-30790
2023-07-01 17:41:55 +02:00
Sam Brannen 3c05679a97 Polishing
See gh-30762
2023-06-30 14:04:37 +02:00
Yanming Zhou d8729a7c67 Polish variable name in ReschedulingRunnable
As a variable name, `initDelay` is applicable for `scheduleAtFixedRate`
and `scheduleWithFixedDelay` but not for `schedule`.

Closes gh-30762
2023-06-30 13:57:46 +02:00
Sam Brannen c95426a616 Polishing 2023-06-30 13:55:34 +02:00
Juergen Hoeller 1e403d1606 Upgrade to Groovy 4.0.13 and Checkstyle 10.12.1 2023-06-30 12:58:28 +02:00
Juergen Hoeller f1567fb21a Raise beforeCompletion/afterCompletion exception log level to error
Closes gh-30776
2023-06-30 12:47:30 +02:00
Juergen Hoeller 60865eae4b Align ConcurrentMapCacheManager locking behavior with CaffeineCacheManager
Closes gh-30780
2023-06-30 10:39:53 +02:00
Juergen Hoeller 0c39fff831 Polishing 2023-06-29 18:04:08 +02:00
Juergen Hoeller e902f9551a Improve condition assertions 2023-06-29 12:05:07 +02:00
Juergen Hoeller 0a20c8a44a Restore defensive access to volatile field in getBeanClassName()
Closes gh-30773
2023-06-29 12:04:53 +02:00
Juergen Hoeller 3cb746c358 Consistently handle invocation exceptions in TypeProxyInvocationHandler
Closes gh-30764
2023-06-28 15:45:40 +02:00
Juergen Hoeller 6526e79eea Polishing 2023-06-26 19:28:38 +02:00
Juergen Hoeller b77d4d01c5 Adapt no-arg value from interface-based InvocationHandler callback
Closes gh-30756
2023-06-26 19:28:19 +02:00
Juergen Hoeller 599ac58baa Avoid arithmetic overflow for large delay/period values
Closes gh-30754
2023-06-26 19:28:08 +02:00
Juergen Hoeller 449174c7d4 Polishing 2023-06-26 12:35:09 +02:00
Juergen Hoeller 9266e6d29e Remove outdated javadoc notes on getMessage and printStackTrace
Closes gh-30748
2023-06-26 12:34:59 +02:00
Juergen Hoeller 062d701ae1 Consistently use mutable ArrayList for modulesToInstall vs modules
Closes gh-30751
2023-06-26 12:34:54 +02:00
Sébastien Deleuze 7137b22e6b Fix test compilation warnings
Closes gh-30753
2023-06-26 12:03:27 +02:00
Sam Brannen acb786d359 Improve logging in MicrometerObservationRegistryTestExecutionListener
Prior to this commit, dependency checks in the static initialization
block for MicrometerObservationRegistryTestExecutionListener resulted
in an ExceptionInInitializerError which led to verbose logging in
TestContextFailureHandler.

This commit improves the logging for missing dependencies in
MicrometerObservationRegistryTestExecutionListener by moving the
dependency checks to the constructor and by throwing a
NoClassDefFoundError instead of an IllegalStateException. This allows
TestContextFailureHandler to log a concise DEBUG message denoting that
the listener is being skipped due to missing dependencies.

This commit also now checks for the presence of
io.micrometer.context.ThreadLocalAccessor in addition to
io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor.

Furthermore, this commit now explicitly mentions the need for
io.micrometer:context-propagation in the error message.

The following demonstrate the generated DEBUB message when
ObservationThreadLocalAccessor and ThreadLocalAccessor are missing,
respectively.

Skipping candidate TestExecutionListener [org.springframework.test.context.observation.MicrometerObservationRegistryTestExecutionListener] due to a missing dependency. Specify custom TestExecutionListener classes or make the default TestExecutionListener classes and their required dependencies available. Offending class: io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor. MicrometerObservationRegistryTestExecutionListener requires io.micrometer:micrometer-observation:1.10.8 or higher and io.micrometer:context-propagation:1.0.3 or higher.

Skipping candidate TestExecutionListener [org.springframework.test.context.observation.MicrometerObservationRegistryTestExecutionListener] due to a missing dependency. Specify custom TestExecutionListener classes or make the default TestExecutionListener classes and their required dependencies available. Offending class: io.micrometer.context.ThreadLocalAccessor. MicrometerObservationRegistryTestExecutionListener requires io.micrometer:micrometer-observation:1.10.8 or higher and io.micrometer:context-propagation:1.0.3 or higher.

Closes gh-30747
2023-06-25 18:49:57 +02:00
Sam Brannen fa7300c1de Remove unused test code and polish 2023-06-25 15:31:15 +02:00
Sam Brannen db17a97ce8 Polishing 2023-06-25 15:18:59 +02:00
Johnny Lim 55f946c5a0 Fix incomplete AssertJ assertions
Closes gh-30743
2023-06-25 15:18:52 +02:00
Sam Brannen 3181dca5ef Support [package-]private init/destroy methods in AOT mode
Prior to this commit, private (and non-visible package-private)
init/destroy methods were not supported in AOT mode. The reason is that
such methods are tracked using their fully-qualified method names, and
the AOT support for init/destroy methods previously did not take
fully-qualified method names into account. In addition, the invocation
order of init/destroy methods differed vastly between standard JVM mode
and AOT mode.

This commit addresses these issues in the following ways.

- AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(),
  DisposableBeanAdapter.determineDestroyMethod(), and
  BeanDefinitionPropertiesCodeGenerator.addInitDestroyHint() now parse
  fully-qualified method names to locate the correct init/destroy
  methods.

- AbstractAutowireCapableBeanFactory and DisposableBeanAdapter delegate
  to a new MethodDescriptor record which encapsulates the parsing of
  fully-qualified method names; however,
  BeanDefinitionPropertiesCodeGenerator duplicates this logic since it
  resides in a different package, and we do not currently want to make
  MethodDescriptor public.

- Init/destroy methods detected via annotations (such as @PostConstruct
  and @PreDestroy) are now invoked prior to init/destroy methods that
  are explicitly configured by name or convention. This aligns with the
  invocation order in standard JVM mode; however,
  InitializingBean#afterPropertiesSet() and DisposableBean#destroy()
  are still invoked before annotated init/destroy methods in AOT mode
  which differs from standard JVM mode.

- Unit and integration tests have been updated to test the revised
  behavior.

Closes gh-30692
2023-06-24 14:22:17 +02:00
Sam Brannen f86a69ebfb Update copyright headers and polish 2023-06-24 14:14:02 +02:00
Sam Brannen 6d63890c56 Use Assumptions.abort() where appropriate 2023-06-24 14:13:22 +02:00
Sam Brannen 489c89b912 Remove unused test code 2023-06-24 14:12:58 +02:00
Sam Brannen 39bc7566df Stop printing to System.out in tests 2023-06-24 14:10:12 +02:00
Sébastien Deleuze d3a249e34d Reduce the delay used for Coroutines in tests
Closes gh-30731
2023-06-23 14:17:56 +02:00
Sébastien Deleuze 81f1edbaf2 Change InvocableHandlerMethod#invokeSuspendingFunction return type
This commits changes the return type from Publisher<?> to
Object in order to avoid potential compatibility issues when
the Reactive Streams dependency is not in the classpath.

Closes gh-30716
2023-06-23 14:17:56 +02:00
Sébastien Deleuze 23ecb50137 Optimize KotlinReflectionParameterNameDiscoverer
This commit removes the intermediate list allocation.

Closes gh-30725
2023-06-23 14:17:56 +02:00
Sam Brannen 3d33d2baa9 Polish contribution 2023-06-23 13:35:16 +02:00
Xiu Hong Kooi 3745224646 Remove redundant assertion in ReactorServerHttpResponse constructor
See gh-30686
Closes gh-30696
2023-06-23 13:34:31 +02:00
Sam Brannen 3bf78d6f8c Fix AbstractMessageListenerContainer Javadoc regarding error log level
This commit updates AbstractMessageListenerContainer's Javadoc
regarding the log level used in invokeErrorHandler() so that the
documentation aligns with the implementation, namely that errors will
logged at WARN level if no ErrorHandler has been registered.

Closes gh-30730
2023-06-23 12:45:52 +02:00
Sam Brannen 563b2a8505 Polish AbstractMessageListenerContainer Javadoc 2023-06-23 12:44:57 +02:00
Sam Brannen 9ccbeec947 Ignore null message when introspecting resource cleanup failure
This commit fixes a regression introduced in conjunction with gh-27572.

See gh-30597
Closes gh-30729
2023-06-23 12:01:16 +02:00
Sam Brannen 29248dff15 Use correct ClassLoader in MicrometerObservationRegistryTestExecutionListener
In the original implementation of
MicrometerObservationRegistryTestExecutionListener I accidentally
imported JUnit 5's org.junit.platform.launcher.TestExecutionListener
instead Spring's
org.springframework.test.context.TestExecutionListener. The code
therefore attempts to use the ClassLoader for the JUnit Platform's
TestExecutionListener which may fail to see the required types. In
addition, if the JUnit Platform's TestExecutionListener is not on the
classpath, the attempt to access its ClassLoader will fail.

This commit addresses this by properly using the ClassLoader for
Spring's TestExecutionListener to detect dependencies of the
MicrometerObservationRegistryTestExecutionListener.

Closes gh-30726
2023-06-22 15:55:46 +02:00
Sam Brannen 65d450ab6d Document that RowCallbackHandler can be used w/ NamedParameterJdbcTemplate
Closes gh-30705
2023-06-22 15:27:41 +02:00
Johnny Lim 271f2dc665 Polish
This commit polishes a bit.

Closes gh-30691
2023-06-22 15:06:05 +02:00
Sam Brannen da323d3335 Update copyright headers 2023-06-22 14:55:23 +02:00
Sam Brannen 32f061a3e0 Polishing 2023-06-22 13:48:07 +02:00
Anton Gubenko 9b5cbc1334 Fix link text from "null" to "Component Classes" in Testing chapter
Closes gh-30714
2023-06-22 11:35:23 +02:00
Johnny Lim 40e378a5a6 Upgrade to me.champeau.jmh 0.7.1
Closes gh-30690
2023-06-22 11:31:32 +02:00
Juergen Hoeller 8eb0a0b94e Upgrade to Tomcat 10.1.10, Netty 4.1.94, Mockito 5.4 2023-06-21 18:52:02 +02:00
Juergen Hoeller 09cf489c6b Test for supportsEventType mismatch with unrelated event type
See gh-30712
2023-06-21 18:24:38 +02:00
Sam Brannen 9a5290ea27 Apply project conventions to formatting 2023-06-21 17:50:42 +02:00
Sam Brannen b3176208e2 Remove invalid Javadoc link 2023-06-21 17:36:36 +02:00
Juergen Hoeller 1dfe737d0e Avoid ResolvableType creation for interface/superclass check
See gh-30713
2023-06-21 17:17:58 +02:00
Juergen Hoeller dc2f513619 Correct supportsEventType checks for payload events
See gh-30712
2023-06-21 17:17:03 +02:00
Sam Brannen 0eb33d09ac Ensure package-private init/destroy methods are always invoked
Prior to this commit, if an init/destroy method was package-private and
declared in a superclass in a package different from the package in
which the registered bean resided, a local init/destroy method with the
same name would effectively "shadow" the method from the different
package, resulting in only the local init/destroy method being invoked.

This commit addresses this issue by tracking package-private init
methods from different packages using their fully-qualified method
names, analogous to the existing support for private init/destroy
methods.

Closes gh-30718
2023-06-21 16:18:19 +02:00
Sam Brannen f67f98a1a7 Polishing 2023-06-21 15:56:50 +02:00
Stephane Nicoll 6c42f374c8 Consistently set target type in generated code
Previously, a bean definition that is optimized AOT could have
different metadata based on whether its resolved type had a generic or
not. This is due to RootBeanDefinition taking either a Class or a
ResolvableType doing fundamentally different things. While the former
sets the bean class which is to little use with an instance supplier,
the latter specifies the target type of the bean.

This commit sets the target type of the bean, using the existing
setter methods that take either a class or a ResolvableType and set the
same attribute consistently.

Closes gh-30689
2023-06-21 14:45:19 +02:00
Sam Brannen 4879b56bb9 Test status quo for private init/destroy methods in AOT mode
This commit only tests which init/destroy methods are "registered" in
AOT mode.

This commit does NOT test that the registered methods can actually be
invoked in AOT mode: that will be addressed in a separate commit.

See gh-30692
2023-06-21 14:19:13 +02:00
Sam Brannen 2fd83aa764 Polish InitDestroyAnnotationBeanPostProcessor internals, etc. 2023-06-21 14:19:00 +02:00
Juergen Hoeller 93218a06ba Cache hasUnresolvableGenerics result for repeated checks
Closes gh-30713
2023-06-21 13:16:04 +02:00
Juergen Hoeller 714c3c59eb Accept unresolvable generics as long as raw event class matches
Closes gh-30712
2023-06-21 13:15:35 +02:00
Juergen Hoeller adcdefce43 Upgrade to Jetty 12.0.0.beta2 2023-06-21 09:49:26 +02:00
Juergen Hoeller 049a024dea Deprecate RootBeanDefinition(ResolvableType) constructor
Closes gh-30704
2023-06-21 09:48:00 +02:00
Juergen Hoeller 20bbebb299 Ensure Spring LogFactory contains all public methods from Apache LogFactory
Closes gh-30668
2023-06-21 09:47:16 +02:00
Sébastien Deleuze 26f006509f Polish CoWebFilter javadoc 2023-06-20 20:56:47 +02:00
Stephane Nicoll 089503aab7 Fix typo 2023-06-20 17:32:24 +02:00
Stephane Nicoll 74155e3d88 Do not invoke AspectJ hints generation if AspectJ is not present
See gh-28711
2023-06-20 17:26:08 +02:00
Sam Brannen 564f33d5ef Polishing 2023-06-20 15:47:41 +02:00
Stephane Nicoll 83acd5b050 Document JPA configuration best practices with AOT
Closes gh-30498
2023-06-20 15:21:12 +02:00
Sam Brannen 67798a7b52 Fix CSS classes in Javadoc HTML tables due to upgrade to Java 17
Closes gh-30701
2023-06-20 13:24:03 +02:00
Stephane Nicoll 2b981651e1 Merge pull request #30601 from cwatzl
* pr/30601:
  Update copyright year of changed file
  Flag PathResourceResolver#resolve*Internal as @Nullable

Closes gh-30601
2023-06-19 15:47:12 +02:00
Stephane Nicoll 294cdba80c Update copyright year of changed file
See gh-30601
2023-06-19 15:44:04 +02:00
cwatzl 072a86149d Flag PathResourceResolver#resolve*Internal as @Nullable
See gh-30601
2023-06-19 15:37:16 +02:00
Sam Brannen 8bb4c167e4 Polishing
See gh-30280
2023-06-17 13:36:37 +02:00
Juergen Hoeller dff7aa4d4b Fall back to type-based creation if no bean of the given name exists
Closes gh-30683
2023-06-17 11:38:57 +02:00
Juergen Hoeller c634acd9ff Recognize error code 2628 as data integrity violation (MSSQL 2019)
Closes gh-30681
2023-06-17 11:38:41 +02:00
Sam Brannen ed5c19f53e Document that @Sql requires spring-jdbc & spring-tx on the classpath
Closes gh-30280
2023-06-16 13:39:35 +02:00
Sébastien Deleuze 03420f811b Add reflection hints for AspectJ advice methods
Closes gh-28711
2023-06-16 11:33:57 +02:00
Sam Brannen 4565bcd757 Update copyright headers 2023-06-15 16:19:58 +02:00
Sam Brannen f22f439a68 Suppress deprecation warning due to upgrade to context-propagation 1.0.3
See gh-30657
2023-06-15 16:18:38 +02:00
Sam Brannen 5672284f53 Remove code duplication in RootBeanDefinition 2023-06-15 16:11:40 +02:00
Spring Builds 367f381fea Next development version (v6.0.11-SNAPSHOT) 2023-06-15 08:31:33 +00:00
Juergen Hoeller b9221656cc Declare ClassLoader for DeserializingConverter constructor as nullable
Closes gh-30670
2023-06-14 22:27:07 +02:00
Juergen Hoeller c30f6aa427 Polishing 2023-06-14 22:17:39 +02:00
Sam Brannen aa2028127f Overhaul TestExecutionListener for Micrometer ObservationRegistry
This commit overhauls the TestExecutionListener for Micrometer's
ObservationRegistry that was introduced in the previous commit.

Specifically, this commit:

- Renames the listener to MicrometerObservationRegistryTestExecutionListener
  since the use of a ThreadLocal is an implementation detail that may
  change over time.

- Makes the listener package-private instead of public in order to
  allow the team greater flexibility in evolving this feature.

- Eagerly loads the ObservationThreadLocalAccessor class and verifies
  that it has a getObservationRegistry() method to ensure that the
  listener is properly skipped when SpringFactoriesLoader attempts to
  load it, if Micrometer 1.10.8+ is not on the classpath.

- Switches the listener's automatic registration order to 2500 in order
  to register it after the DependencyInjectionTestExecutionListener.

- Only tracks the previous ObservationRegistry in beforeTestMethod() if
  the test's ApplicationContext contains an ObservationRegistry bean.

- Properly removes the TestContext attribute for the previous
  ObservationRegistry in afterTestMethod().

- Introduces DEBUG logging for diagnostics.

- Adds an entry in the Javadoc for TestExecutionListener as well as in
  the Testing chapter in the reference manual.

Closes gh-30658
2023-06-14 14:36:05 +02:00
Marcin Grzejszczak a82659c837 Introduce TestExecutionListener for Micrometer ObservationRegistry
Prior to this commit, there was no way to specify the
ObservationRegistry that is registered in the given test's
ApplicationContext as the one that should be used by Micrometer's
ObservationThreadLocalAccessor for context propagation.

This commit introduces a TestExecutionListener for Micrometer's
ObservationRegistry in the Spring TestContext Framework. Specifically,
this listener obtains the ObservationRegistry registered in the test's
ApplicationContext, stores it in ObservationThreadLocalAccessor for the
duration of each test method execution, and restores the original
ObservationRegistry in ObservationThreadLocalAccessor after each test.

Co-authored-by: Sam Brannen <sam@sambrannen.com>
See gh-30658
2023-06-14 14:35:53 +02:00
Juergen Hoeller 3415b04c73 Use nanosecond precision for scheduling (aligned with calculations)
Closes gh-30666
2023-06-14 12:57:17 +02:00
Arjen Poutsma a73ad52a8a Clarify removal of ResponseEntity::getStatusCodeValue 2023-06-14 11:29:21 +02:00
Juergen Hoeller 13c32d80ba Upgrade to Undertow 2.3.7 and JRuby 9.4.3 2023-06-14 11:18:37 +02:00
Juergen Hoeller 3171a8b0e2 Align with 5.3.28 javadoc for isSimpleValueType
See gh-30661
2023-06-14 11:18:18 +02:00
Juergen Hoeller 927d27b121 Consider UUID as simple value type with concise toString output
Closes gh-30661
2023-06-14 09:31:35 +02:00
Juergen Hoeller 1df5e9f30c Upgrade to Reactor 2022.0.8
Closes gh-30655
2023-06-14 09:31:02 +02:00
Sam Brannen ed74b04520 Move web-related test for BeanUtilsRuntimeHints to spring-web
The dependency on spring-web from spring-beans makes it impossible to
import the projects in Eclipse IDE due to cycles between projects.

This commit therefore moves the web-related test for
BeanUtilsRuntimeHints to spring-web.

See gh-30491
2023-06-13 17:12:38 +02:00
Sam Brannen 6cfb11a360 Upgrade to Micrometer BOM 1.10.8 and context-propagation 1.0.3
Cloes gh-30657
2023-06-13 16:21:26 +02:00
Juergen Hoeller 3b263c5c3d Synchronize transaction begin for non-lazy default transactions as well
Lazy and read-only database transactions remain exposed to potential isolation level mismatches in case of concurrent transactions with custom isolation levels.

Closes gh-29997
2023-06-13 12:40:57 +02:00
Sam Brannen f18a85e193 Polish null-safety documentation 2023-06-13 11:57:41 +02:00
rstoyanchev f7d195782f Revert "Temporarily re-enable ReactorNetty2StompBrokerRelayIntegrationTests"
This reverts commit 7e52b803be.
2023-06-12 16:11:42 +01:00
rstoyanchev bbab4faf7a Method level only, empty RequestMapping matches "" and "/"
Closes gh-30293
2023-06-12 16:11:42 +01:00
Sam Brannen 3fb98b6a97 Polishing 2023-06-12 16:43:56 +02:00
Roland Weisleder dc4f46df9f Fix markup issue in Javadoc of QuartzCronField
Closes gh-30646
2023-06-12 16:40:11 +02:00
Sébastien Deleuze 80af7bedef Polish null-safety reference documentation 2023-06-12 16:00:13 +02:00
Juergen Hoeller f8c8873c99 Document which @Scheduled attributes support SpEL expressions
Closes gh-29290
2023-06-12 12:57:10 +02:00
Juergen Hoeller cc8c852c2b Specific check for parent of MethodInvocationInfo ClassLoader
See gh-30389
2023-06-12 12:57:10 +02:00
Sébastien Deleuze fe7f8e2de5 Polish CoWebFilter 2023-06-12 12:48:01 +02:00
Juergen Hoeller 0a5aff1b60 Specific check for parent of spring-aop ClassLoader
Also applied to getProxyClass now.

Closes gh-30389
2023-06-12 10:48:57 +02:00
Vedran Pavic 3d61d9e0d8 Handle custom JMS acknowledgment modes as client acknowledge (#30619)
This commit updates JmsAccessor to handle custom JMS acknowledgment
modes as client acknowledge, which is useful when working with JMS
providers that provide non-standard variations of CLIENT_ACKNOWLEDGE,
such as AWS SQS and its UNORDERED_ACKNOWLEDGE mode.
2023-06-12 10:46:22 +02:00
Sam Brannen 1a26e17f41 Ensure context cache stats are logged when ApplicationContext fails to load
Closes gh-30635
2023-06-11 15:46:28 +02:00
Sébastien Deleuze 439bcd6715 Polishing 2023-06-09 15:19:42 +02:00
Sébastien Deleuze 4e33d0c293 Add missing hint for ResourceEditor
Closes gh-30628
2023-06-09 15:19:18 +02:00
Sébastien Deleuze 9b4e0e9837 Add missing hint for converting String to URI
Closes gh-30627
2023-06-09 14:14:54 +02:00
Sébastien Deleuze 071d6a2a5a Introduce hints for finding hints by conventions
Closes gh-30491
2023-06-09 14:14:54 +02:00
Sam Brannen d7d0292654 Document @DirtiesContext semantics when declared at class & method level
Closes gh-30623
2023-06-09 11:49:15 +02:00
Sam Brannen 2817dce78f Polish @DirtiesContext Javadoc 2023-06-09 11:40:31 +02:00
Sam Brannen 698acd32a7 Configure logger per concrete @DirtiesContext TestExecutionListener 2023-06-09 11:38:36 +02:00
Sam Brannen f36327b380 Exclude commons-logging dependency in spring-test 2023-06-09 11:37:08 +02:00
Johnny Lim ea9c0ae6c0 Fix test fixture value in QualifierConfiguration
Closes gh-30624
2023-06-09 10:29:39 +02:00
Juergen Hoeller c16f582ed8 Consistent equals implementations in AOP support classes 2023-06-08 17:42:49 +02:00
Juergen Hoeller 6931106c5e Redesign inner Pointcut implementations as standalone classes
Avoids exposure of implicit Advisor instance state in Pointcut instance.

See gh-30621
2023-06-08 17:42:49 +02:00
Juergen Hoeller 045df81f14 Reduce ProxyCallbackFilter to key-only role after class generation
Avoids memory leaks from ProxyCallbackFilter-contained Advisors.
Includes consistent ProxyCallbackFilter#equals/hashCode methods.

Closes gh-26266
Closes gh-30615
2023-06-08 17:42:49 +02:00
Juergen Hoeller e210f08dce Declare Advisor#isPerInstance() as default method
Includes INSTANCE constants on default factory classes.

Closes gh-30614
2023-06-08 17:42:49 +02:00
Vedran Pavic 2317bef021 Add missing @Nullable in DefaultJmsListenerContainerFactory
Closes gh-30620
2023-06-08 17:32:59 +02:00
Arjen Poutsma ce5189a0a0 Default ServerWebExchange::cleanupMultipart implementation
This commit provided a default implementation for ServerWebExchange::cleanupMultipart.

See gh-30590
2023-06-08 16:31:53 +02:00
Johnny Lim 7a4ed38cd4 This PR adds Javadoc for RegisteredBean.resolveAutowiredArgument()
See gh-30401
Closes gh-30609
2023-06-08 16:06:14 +02:00
Arjen Poutsma c1fe57135e Clean multipart up only when data is read
This commit ensures that any storage used for multipart handling only
gets cleaned up if multipart data is actually retrieved via
ServerWebExchange::getMultipartData.

Closes gh-30590
2023-06-08 14:23:47 +02:00
Juergen Hoeller 2c8d1b7bff Polishing 2023-06-07 17:21:59 +02:00
Juergen Hoeller bd63c35b0b DefaultMessageListenerContainer tests for stop-and-restart
See gh-30612
2023-06-07 17:21:53 +02:00
Juergen Hoeller c052a02592 Ignore null message when introspecting resource cleanup failure
Closes gh-30597
2023-06-07 17:21:41 +02:00
Sam Brannen 5ad853ef5b Do not precede indexed access with a dot in SpEL AST representation
Prior to this commit, if a Spring Expression Language (SpEL) expression
contained indexed access to an object, the generated AST String
representation incorrectly included a dot ('.') before the index access.

For example, 'property[0]' had a generated AST string representation of
'property.[0]'.

This commit addresses this by reworking the logic in
CompoundExpression.toStringAST().

Closes gh-30610
2023-06-07 14:45:52 +02:00
Sam Brannen dfbed616ba Update copyright headers 2023-06-07 13:17:14 +02:00
Sam Brannen 94214562d0 Polish contribution
See gh-30283
2023-06-07 13:15:12 +02:00
SW be94e1a2fb Replace switch statements with enhanced switch statements for consistency
Closes gh-30283
2023-06-07 13:02:08 +02:00
Sam Brannen f2ae106c32 Update deprecation Javadoc regarding "for removal in 6.2"
See gh-30608
2023-06-07 12:00:27 +02:00
Sam Brannen 7b20aefecf Improve error message for unsupported character in SpEL expression
Prior to this commit, when an unsupported character such as "ü" was
encountered in a SpEL expression, the error message was:

Cannot handle (252) 'ü'

With this commit, the error message is now similar to:

Unsupported character 'ü' (252) encountered at position 5 in expression.

See gh-30580
Closes gh-30602
2023-06-06 12:45:05 +02:00
Juergen Hoeller cdc4497664 Restore creation of plain HashSet/HashMap for direct HashSet/HashMap type
Closes gh-30596
2023-06-05 13:57:59 +02:00
Juergen Hoeller 6cc084dbde Consistent javadoc references to JdbcTransactionManager 2023-06-05 10:52:33 +02:00
Juergen Hoeller 974e10379a Add nested propagation support to R2dbcTransactionManager
Closes gh-30134
2023-06-05 10:45:47 +02:00
Juergen Hoeller 22ef48bec2 Shorten constant names to BUILD and RUN (for consistent spelling)
See gh-30511
2023-06-05 10:45:02 +02:00
Juergen Hoeller 4be813bbee Upgrade to Groovy 4.0.12, Tomcat 10.1.9, Netty 4.1.93, Undertow 2.3.6, Protobuf 3.23.2, Picocli 4.7.4, Checkstyle 10.12 2023-06-04 18:42:21 +02:00
Juergen Hoeller 65617f1e75 Add isolation level test and restore null return value from beginTransaction
See gh-29997
2023-06-04 18:41:38 +02:00
Juergen Hoeller aaebf5749c Set and reset shared isolation value within synchronized transaction begin
Since EclipseLink applies a custom transaction isolation value to its shared DatabasePlatform instance, we need to immediately restore the original value after the current value got picked up for JDBC Connection access inside of EclipseLink. In order to not interfere with concurrent transactions, we need to use synchronization around the transaction begin sequence in such a case.

Closes gh-29997
2023-06-04 17:19:27 +02:00
Sam Brannen 24fa8793b1 Update copyright headers 2023-06-04 16:36:52 +02:00
Sam Brannen 4f10f559f7 Suppress warnings 2023-06-04 16:36:20 +02:00
Sam Brannen 2f35e7756b Integration test status quo for SpEL 'selector' support in messaging
See gh-30550
2023-06-04 16:25:21 +02:00
Sam Brannen cc50af08e4 Enhance unit tests for status quo for SpEL 'selector' support in messaging
See gh-30550
2023-06-04 16:23:42 +02:00
Sam Brannen d08e4081c2 Polishing 2023-06-04 15:59:12 +02:00
Juergen Hoeller 9751987dc1 Avoid unnecessary auto-commit check for proper transaction begin
Closes gh-30508
2023-06-03 17:17:47 +02:00
Juergen Hoeller d2906253f1 Fall back to plain setObject call for non-supported SQL type
Closes gh-30556
2023-06-03 17:16:36 +02:00
Juergen Hoeller 4b8adf2dcc Polishing 2023-06-02 23:28:14 +02:00
Juergen Hoeller c68552556f Revise TargetSource implementations for proper nullability
Includes hashCode optimization in AbstractBeanFactoryBasedTargetSource.
Includes ThreadLocal naming fix in ThreadLocalTargetSource.

Closes gh-30576
Closes gh-30581
2023-06-02 23:26:58 +02:00
Juergen Hoeller b738a20233 Consistently publish events from CompletableFuture
Closes gh-30578
2023-06-02 23:25:35 +02:00
Juergen Hoeller cca8fbd3de Log warning in case of standard Commons Logging discovery
Closes gh-30575
2023-06-02 23:25:08 +02:00
Sam Brannen 21397a67c6 Test status quo for SpEL 'selector' support in messaging
Prior to this commit, the tests we had in place for SpEL 'selector'
support did not assert what happens when a selector expression does not
match or when a selector header is not present.

See gh-30550
2023-06-01 14:57:50 +02:00
Sam Brannen e8ab53e76d Polishing 2023-06-01 14:57:50 +02:00
Stephane Nicoll 5b471a5349 Merge pull request #30570 from kihyuk-sung
* pr/30570:
  Polish "Fix invalid link in transaction resources"
  Fix invalid link in transaction resources

Closes gh-30570
2023-05-31 12:26:48 +02:00
Stephane Nicoll 08240bbcac Polish "Fix invalid link in transaction resources"
See gh-30570
2023-05-31 12:26:32 +02:00
K d719f3fc06 Fix invalid link in transaction resources
See gh-30570
2023-05-31 12:20:06 +02:00
rstoyanchev 938dff7bbe Remove (recently committed) erroneous character in test 2023-05-30 17:35:50 +01:00
rstoyanchev 162ccdd155 Consistent handling of parts in HttpRequestValues
Closes gh-30520
2023-05-30 17:17:31 +01:00
rstoyanchev f1594312cd Polishing in HttpRequestValues and tests
See gh-30520
2023-05-30 17:17:31 +01:00
rstoyanchev 7629ea5672 Update docs to mention Boot's HttpMessageConverters
Closes gh-30538
2023-05-30 17:17:31 +01:00
rstoyanchev 454a85978f Allow fallback on subclass of ProblemDetail
Closes gh-30533
2023-05-30 17:17:31 +01:00
Sébastien Deleuze 2cbc7eed73 Always use MethodArgumentNotValidException(MethodParameter, BindingResult) constructor
gh-23846 introduced a new
MethodArgumentNotValidException(Executable, BindingResult)
constructor that can be advantageously replaced by using
MethodArgumentNotValidException(MethodParameter, BindingResult)
in ModelAttributeMethodProcessor.

This commit updates ModelAttributeMethodProcessor accordingly,
and deprecates MethodArgumentNotValidException(Executable,
BindingResult) in favor of
MethodArgumentNotValidException(MethodParameter, BindingResult).

Closes gh-30558
2023-05-30 12:41:30 +02:00
Sébastien Deleuze 7a05f81844 Polish ModelAttributeMethodProcessor#constructAttribute Javadoc 2023-05-30 12:07:52 +02:00
Brian Clozel 71b2154ec8 Remove libs-spring-framework-build repository
This commit removes the build configuration for the repository
"libs-spring-framework-build", as it is not needed anymore for the
dependencies it contains.
2023-05-30 10:36:53 +02:00
Stephane Nicoll a716391aab Merge pull request #30554 from scordio
* pr/30554:
  Update copyright year of changed file
  Fix `FileSystemUtils::deleteRecursively` Javadoc

Closes gh-30554
2023-05-30 09:15:43 +02:00
Stephane Nicoll 09b2e5d0fb Update copyright year of changed file
See gh-30554
2023-05-30 09:13:29 +02:00
Stefano Cordio c6b2d2e1d9 Fix FileSystemUtils::deleteRecursively Javadoc
See gh-30554
2023-05-30 09:11:48 +02:00
Sébastien Deleuze 1cf6d1dd9d Refine GraalVM tracing agent detection
This commit refines how GraalVM tracing agent detection works
for both test and application executions.

It rolls back the introduction of TestAotDetector done in 111309605c
and instead updates AotDetector.useGeneratedArtifacts()
to only detect "buildtime" and "runtime" imagecode system
property values by leveraging a new method
NativeDetector.inNativeImage(NativeDetector.Context...).

This commit also adds a workaround for
https://github.com/oracle/graal/issues/6691.

Closes gh-30511
2023-05-26 19:10:21 +02:00
Juergen Hoeller 8b8d147480 Avoid Autowired shortcut resolution for NullBean values
Includes getBean documentation against NullBean values.

Closes gh-30485
2023-05-26 11:03:10 +02:00
Brian Clozel 8fe21b8ef9 Build Antora docs as part of CI check job
This commit adds the "antora" task to the check-project script in order
to check for documentation failures when pushing to the current branch.
The actual documentation is build is performed on the docs-build branch.

Closes gh-30528
2023-05-25 20:12:04 +02:00
Brian Clozel 7c9f5d05db Remove asciidoctor Gradle plugins
This part of the build has been replaced with Antora.
2023-05-25 20:07:50 +02:00
Brian Clozel 110b809b7d Update CI pipeline 2023-05-25 19:15:32 +02:00
Juergen Hoeller 5e625c8d2e Polishing 2023-05-25 18:45:32 +02:00
Juergen Hoeller 3f648f54c5 Set hibernate.cdi.extensions (for SpringBeanContainer on Hibernate 6.2)
Closes gh-30545
2023-05-25 18:45:27 +02:00
Sam Brannen d61074268a Polish Javadoc for StatementFilterFunction 2023-05-25 13:58:00 +02:00
Sam Brannen cfb3a45479 Improve Javadoc for ExchangeFilterFunction
See gh-30543
2023-05-25 13:47:37 +02:00
galingerv 7d7d7bcced Fix typos in Javadoc for ExchangeFilterFunction
Closes gh-30543
2023-05-25 13:40:18 +02:00
Sam Brannen 6dfd330fa5 Polish JavaMailSenderTests 2023-05-24 16:54:44 +02:00
Evgeny Nikonchuk 7f9349b7ae Remove non-empty filename check in ContentDisposition builder
This commit ensures that the ContentDisposition.Builder is consistent
with ContentDisposition and accepts null/empty filenames.

Closes gh-30537
2023-05-24 13:53:46 +02:00
Juergen Hoeller 4c8f1910c8 Align HibernateJpaVendorAdapter with Hibernate ORM 6.2
Includes documentation and test revisions.

Closes gh-30288
2023-05-23 20:33:19 +02:00
rstoyanchev 3b364c2a3d Add cached-antora-playbook.yml back to .gitignore
See gh-30481
2023-05-23 15:12:35 +01:00
rstoyanchev 0930407d0e Revert "Download the playbook to build dir"
This reverts commit d8f6567c89.

See gh-30481
2023-05-23 15:05:11 +01:00
Sam Brannen 4bb12c4ba4 Update copyright headers 2023-05-23 15:10:08 +02:00
Sam Brannen 8d6d99731f Delete unused code 2023-05-23 15:09:38 +02:00
Sam Brannen b5902f4fbf Suppress unused warnings in tests 2023-05-23 15:09:10 +02:00
Sam Brannen a455317122 Treat directly registered class as configuration 'lite' mode candidate
Prior to this commit, if a non-annotated [1] class was registered
directly with an ApplicationContext -- for example, via
AnnotatedBeanDefinitionReader, AnnotationConfigApplicationContext, or
@ContextConfiguration -- it was not considered a configuration class in
'lite' mode unless @Bean methods were declared locally. In other words,
if the registered class didn't declare local @Bean methods but rather
extended a class that declared @Bean methods, then the registered class
was not parsed as a @Configuration class in 'lite' mode, and the @Bean
methods were ignored.

Whereas, a non-annotated class registered via @Import is always
considered to be a configuration class candidate.

To address this discrepancy between @Import'ed classes and classes
registered directly with an ApplicationContext, this commit treats any
class registered via AnnotatedBeanDefinitionReader as a @Configuration
class candidate with @Bean 'lite' mode semantics.

[1] In this context, "non-annotated" means a class not annotated (or
meta-annotated) with @Component, @ComponentScan, @Import, or
@ImportResource.

Closes gh-30449
2023-05-23 12:19:58 +02:00
Sam Brannen a8b400803b Upgrade to TestNG 7.8.0 2023-05-23 11:36:25 +02:00
greg.lee fa85657801 Introduce method in MockHttpServletRequestBuilder to set remote address
Co-authored-by: Sam Brannen <sam@sambrannen.com>
See gh-30510
Closes gh-30497
2023-05-23 11:26:26 +02:00
Sébastien Deleuze 424daede2f Add missing EntityManagerFactory hints for SharedEntityManagerCreator
Closes gh-30523
2023-05-23 11:18:54 +02:00
Sébastien Deleuze e92c9c5619 Disable Hibernate reflection optimizer with native
Closes gh-30521
2023-05-23 09:10:47 +02:00
Stephane Nicoll 7660dbfd77 Merge pull request #30512 from izeye
* pr/30512:
  Add Javadoc since to InjectedElement.shouldInject()

Closes gh-30512
2023-05-22 15:30:33 +02:00
Johnny Lim 15a7f43c7b Add Javadoc since to InjectedElement.shouldInject()
See gh-30512
2023-05-22 15:30:05 +02:00
Simon Baslé 1721e42988 Remove JBossAS catch-all javadoc external link
This commit removes the JBoss Application Server external javadoc link,
which covers too many packages and can thus problematically take
precedence for said packages and lead to dead links.

It is replaced by Hibernate javadocs for `org.hibernate.*` packages,
JakartaEE and jsr305 for annotations. We lose a limited number of links
around org.xnio classes and Hibernate-and-JTA-related classes, as a
trade-off.

See gh-30455
See gh-30428
2023-05-16 15:04:34 +02:00
Simon Baslé eabb846d07 Improve how the build deals with javadoc invalid references
This commit improves how the build deals with javadoc invalid references
in two ways.

Link/see references that are temporarily invalid during javadoc
generation of individual modules are better masked by using the option
`Xdoclint:syntax` instead of `Xdoclint:none` (warnings were still
visible in some cases, e.g. when individually building the javadoc for
a specific module).

Global javadoc-building task `api` now combines `syntax` and `reference`
`Xdoclint` groups, allowing to raise truly invalid references even when
all the modules have been aggregated.

This commit also fixes the 20+ errors which appeared following the later
change in doclet configuration.

Closes gh-30428
2023-05-16 15:04:10 +02:00
Jan Mewes 280b0de646 Docs: Remove duplicate in SpEL feature list
Closes gh-30404
2023-05-15 15:22:44 +02:00
Sam Brannen 2738b3e50f Polishing
See gh-30482
2023-05-15 15:09:06 +02:00
Anyuruf 169795d673 Fix wording in r2dbc Statement Filters section of ref docs
Co-authored-by: Sam Brannen <sam@sambrannen.com>
Closes gh-30482
2023-05-15 15:08:58 +02:00
Sébastien Deleuze 085565a771 Skip class transformer in PersistenceUnitInfoDescriptor for native images
In order to restore native support with Hibernate 6.2, this
change updates the PersistenceUnitInfoDescriptor instance
created by SpringHibernateJpaPersistenceProvider in order to
skip the class transformer for native images.

Closes gh-30492
2023-05-15 11:48:56 +02:00
rstoyanchev 806519f78a Comment out check->antora dependency
See gh-30481
2023-05-12 10:19:52 +01:00
Stephane Nicoll c3c5eaf914 Align AOT contributions for injection with the runtime behavior
Bean post processors that use InjectionMetadata checks if a property
value for the element it is about to inject is set and skip it, so
that the property value is used. Previously, the AOT contribution for
the same behavior did not check if a matching property value is set
and therefore override the user-defined value.

This commit introduces an additional method that filters the injected
element list so that only the elements that should be processed are
defined.

Closes gh-30476
2023-05-12 08:49:22 +02:00
Stephane Nicoll a9b94241af Fix typo
Closes gh-30473
2023-05-11 16:35:20 +02:00
Spring Builds 7066dc4ba7 Next development version (v6.0.10-SNAPSHOT) 2023-05-11 12:47:00 +00:00
Juergen Hoeller 3f889e997c Clarify RootBeanDefinition vs GenericBeanDefinition
Closes gh-30444
2023-05-10 18:50:27 +02:00
Juergen Hoeller 6622a3c7d6 Consistent package-info declarations for CGLIB fork
Closes gh-30461
2023-05-10 18:50:17 +02:00
Rob Winch d8f6567c89 Download the playbook to build dir
Closes gh-30465
2023-05-10 17:39:40 +01:00
Sam Brannen 111309605c Support Test AOT processing with GraalVM tracing agent and NBT
Prior to this commit, test AOT processing failed when using the GraalVM
tracing agent and GraalVM Native Build Tools (NBT) plugins for Maven
and Gradle.

The reason is that the AOT support in the TestContext framework (TCF)
relied on AotDetector.useGeneratedArtifacts() which delegates
internally to NativeDetector.inNativeImage() which does not
differentiate between values stored in the
"org.graalvm.nativeimage.imagecode" JVM system property.

This commit addresses this issue by introducing a TestAotDetector
utility that is specific to the TCF. This detector considers the
current runtime to be in "AOT runtime mode" if the "spring.aot.enabled"
Spring property is set to "true" or the GraalVM
"org.graalvm.nativeimage.imagecode" JVM system property is set to any
non-empty value other than "agent".

Closes gh-30281
2023-05-10 17:39:46 +02:00
Sam Brannen 89bcee68bb Add author tag 2023-05-10 17:39:46 +02:00
Juergen Hoeller c8927ae753 Polishing 2023-05-10 17:18:31 +02:00
Juergen Hoeller e228f4ba64 Consistent pre-resolution of event type vs payload type
Restores proper event type propagation to parent context.
Selectively applies payload type to given payload object.
Also reuses cached type for regular ApplicationEvent now.

Closes gh-30360
2023-05-10 17:17:48 +02:00
Simon Baslé c733ae0f22 Fix ApplicationListenerMethodAdapter#supportsEventType check
This commit fixes the check by avoiding a fallback to eventType's
hasUnresolvableGenerics(). This could previously lead to checking a
generic event type `A<T>` against a listener which accepts unrelated
`B` and return `true` despite the inconsistency.

Note that this wouldn't necessarily surface to the user because there is
a `catch (ClassCastException e)` down the line, which was primarily put
in place to deal with lambda-based listeners but happens to catch an
exception thrown due to the bad result of `supportsEventType`.

The `supportsEventType` now matches generic `PayloadApplicationEvent`
types with a raw counterpart, using the above fallback only in that case
and otherwise ultimately returning `false`.

Closes gh-30399
2023-05-10 15:00:11 +02:00
Sam Brannen aefcb9d2d6 Make maximum SpEL expression length configurable
Closes gh-30380
2023-05-10 14:35:36 +02:00
Stephane Nicoll 120c228b70 Handle injection of several persistence units
This commit adapts the generated code so that each injection point has
a dedicated namespace in the form of a private method. That prevents
the same variable to be reused twice which lead to a compilation failure
previously.

Closes gh-30437
2023-05-10 13:55:26 +02:00
rstoyanchev bd66c70b2b Potential fix for flaky STOMP integration tests
When ReactorNetty2StompBrokerRelayIntegrationTests fail, typically there
are multiple exceptions "Connection refused: /127.0.0.1:61613" that
appear after we've conneted, sent CONNECT, and expecting CONNECTED, but
that does not come within the 10 second timeout.

61613 is the default port for STOMP. However, in all integration tests
we start ActiveMQ with port 0 which results in a random port. Moreover,
the stacktrace is for Netty 4 (not 5), and the eventloop thread id's
are different than the one where the connection to the correct, random
port was established.

The suspicion is that these are log messages from
MessageBrokerConfigurationTests which focuses on testing configuration
but nevertheless as a bean starts and attempts to connect to the default
port and fails. Perhaps those attempts to connect on the default port
somehow affect the ActiveMQ server, and it stops responding.

This change adds a no-op TcpClient in MessageBrokerConfigurationTests
to avoid unnecessary attempts to connect that are not needed.

See gh-29287
2023-05-10 11:42:45 +01:00
rstoyanchev e737980033 Ensure chunks released on cancel in StringDecoder
The current test were not catching the issue because they request 1
via StepVerifier, wait for it, and then cancel. In the case of
StringDecoder it means all chunks are used up to produce that first
String and so the cancel doesn't catch any cached chunks.

Closes gh-30299
2023-05-09 19:58:36 +01:00
Stephane Nicoll 93fa01004c Document AOT best practices
This commit augment the AOT section with best practices we have
experienced while working on the AOT engine. In particular, it describes
how a FactoryBean with an unresolved generic should be handled.

Closes gh-30434
2023-05-09 16:25:36 +02:00
Stephane Nicoll c1f6d7197b Add support for InjectionPoint with AOT
This commit reviews BeanInstanceSupplier to reuse more code from
ConstructorResolver. Previously, the autowired argument resolution was
partially duplicated and this commit introduces a new common path via
RegisteredBean#resolveAutowiredArgument.

Closes gh-30401
2023-05-09 14:52:23 +02:00
Arjen Poutsma a133aae8d6 Document @ResponseStatus behavior
This commit documents that the ResponseStatus annotation does not
override the status set through other means.

Closes gh-30305
See gh-18019
2023-05-09 14:01:33 +02:00
Rob Winch 3a0a19cff8 Fix migration to asciidoctor tabs
The automation to asciidoctor tabs did not migrate properly for all
cases this commit fixes the migration.

See gh-30435
2023-05-09 12:15:43 +01:00
Juergen Hoeller 6930d4d531 Upgrade to Reactor 2022.0.7 and Micrometer 1.10.7
Includes Protobuf 3.23, Woodstox 6.5.1, Caffeine 3.1.6, Picocli 4.7.3, BlockHound 1.0.8, ActiveMQ 5.17.4

Closes gh-30450
Closes gh-30451
2023-05-09 12:55:44 +02:00
Brian Clozel 6bc674b2dd Upgrade CI image to Ubuntu Jammy 2023-05-09 09:34:16 +02:00
Juergen Hoeller 4aa8619ac2 Upgrade to Tomcat 10.1.8, Jetty 11.0.15 / 12.0.0.beta1, Netty 4.1.92, Jackson 2.14.3, Mockito 5.3.1, Checkstyle 10.10 2023-05-08 13:06:19 +02:00
Juergen Hoeller 2c7e8661cf Respect TaskDecorator configuration on DefaultManagedTaskExecutor
Closes gh-30442
2023-05-08 12:02:25 +02:00
Juergen Hoeller c09055b83a Consistent support for MultiValueMap and common Map implementations
Closes gh-30440
2023-05-08 12:01:58 +02:00
Sébastien Deleuze 2c15dcc56b Fix native support for generic array parameters
This commit adds a workaround for oracle/graal#6529
triggered by b374824319.

When the GraalVM fix will have reached a wide enough
audience, it should be removed via gh-30394.

Closes gh-30407
2023-05-05 18:20:08 +02:00
Rob Winch 06f16f15ed Remove PDF Link
See gh-30430
2023-05-05 16:56:25 +01:00
Stephane Nicoll da37deec1b Merge pull request #30423 from izeye
* pr/30423:
  Polish "Introduce internal constants for implicit bounds in TypeUtils"
  Introduce internal constants for implicit bounds in TypeUtils

Closes gh-30423
2023-05-05 15:03:58 +02:00
Stephane Nicoll 88361a4f3e Polish "Introduce internal constants for implicit bounds in TypeUtils"
See gh-30423
2023-05-05 15:01:46 +02:00
Johnny Lim 92cf7f1a7f Introduce internal constants for implicit bounds in TypeUtils
See gh-30423
2023-05-05 14:58:24 +02:00
rstoyanchev 8a8fb31f4a Update target repository for Antora
See gh-30414
2023-05-04 15:37:21 +01:00
Rob Winch 719553f627 Fix Web Reactive link on the landing page 2023-05-04 15:35:05 +01:00
Rob Winch 993447deb0 Remove invalid deploy-docs.yml 2023-05-04 15:35:05 +01:00
Rob Winch 88c9b0f467 Use the shared antora playbook 2023-05-04 15:35:05 +01:00
Rob Winch 39146f9066 Migrate to Asciidoctor Tabs 2023-05-04 15:35:05 +01:00
Rob Winch 71154fd16b Add ./github/workflow/deploy-docs.yml 2023-05-04 15:35:05 +01:00
Rob Winch 7b0ba0188f Structural Fixes 2023-05-04 15:35:05 +01:00
Rob Winch 3774e9be7a Trim IDs with parent 2023-05-04 15:35:05 +01:00
Rob Winch 9c38f8c3c6 Make local build (temporary) 2023-05-04 15:35:05 +01:00
Rob Winch 26171d9efb Fix Title in antora.yml 2023-05-04 15:35:05 +01:00
Rob Winch 68235464fc Use include-code Macro 2023-05-04 15:35:05 +01:00
Rob Winch 51ada4e19d Fix invalid nav elements 2023-05-04 15:35:05 +01:00
Rob Winch 1a8e0031fe Fix index Overview link 2023-05-04 15:35:05 +01:00
Rob Winch d8bcbd53e1 Fix invalid attributes 2023-05-04 15:35:05 +01:00
Rob Winch 6993a9eee4 fix antora.yml attributes 2023-05-04 15:35:05 +01:00
Rob Winch d2720741a5 Fix antora.yml gradle command 2023-05-04 15:35:05 +01:00
Rob Winch 067e6c2a9e fix framework-docs.gradle 2023-05-04 15:35:05 +01:00
Rob Winch 35229c8bff Enable Section Summary TOC for small pages 2023-05-04 15:35:05 +01:00
Rob Winch 139cde47e2 Fix cross references 2023-05-04 15:35:05 +01:00
Rob Winch 6b341ddf19 Remove includes 2023-05-04 15:35:05 +01:00
Rob Winch 5aecfb1878 Generate a default navigation 2023-05-04 15:35:05 +01:00
Rob Winch 9f49d24833 Split files 2023-05-04 15:35:05 +01:00
Rob Winch ac69a5dac3 Copy default antora files 2023-05-04 15:35:05 +01:00
Rob Winch 7f9061a3cb Fix image::image 2023-05-04 15:35:05 +01:00
Rob Winch a05ff6c126 Remove unnecessary asciidoc attributes 2023-05-04 15:35:05 +01:00
Rob Winch 9ceb75b228 Insert explicit ids for headers 2023-05-04 15:35:05 +01:00
Rob Winch 3fe7c65218 Migrate Structure 2023-05-04 15:35:05 +01:00
Rob Winch a78836c18b Remove duplicated testing from .adoc file path 2023-05-04 15:35:05 +01:00
Rob Winch 2a4a1cc550 Remove -languages suffix from .adoc files 2023-05-04 15:35:05 +01:00
Rob Winch c762c07290 Remove integration- prefix from .adoc files 2023-05-04 15:35:05 +01:00
Rob Winch 957467aa32 Remove data-access- prefix from .adoc files 2023-05-04 15:35:04 +01:00
Rob Winch 49a4734cfa Remove core- prefix from .adoc files 2023-05-04 15:35:04 +01:00
Stephane Nicoll 44b427c3e1 Add AOT support for Qualifiers
This commit handles AutowiredCandidateQualifier instances, rather than
relying on qualifiers being statically defined and meta-annotated with
`@Qualifier`.

Closes gh-30410
2023-05-04 12:17:07 +02:00
Sam Brannen aabd685225 Update major/minor version properties in MockServletContext
ServletContext has sets of major/minor version properties that we have
not updated in MockServletContext in several years.

Since we upgraded the baseline to Servlet 6.0 in Spring Framework 6.0,
now seems like a good time to update those version properties.

Closes gh-30395
2023-04-28 15:43:29 +02:00
Sébastien Deleuze cefb734763 Restore native support for record beans
After b374824319 related
to gh-29246, `"queryAllDeclaredMethods": true` is now added
on all registered beans.

This legit change triggers oracle/graal#6510. This
commit workarounds this GraalVM bug, and should be
removed once the GraalVM fix has reached a wide enough
audience.

Closes gh-30383
2023-04-28 09:57:22 +02:00
Stephane Nicoll 82e5464972 Avoid extra "__BeanDefinitions" suffix for inner classes
This commit makes AOT class names for bean definitions shorter.
Previously, the "__BeanDefinitions" suffix was applied for all
classes, but it was unnecessary for inner classes as the container
class already has the qualifier.

Closes gh-29846
2023-04-26 16:52:46 +02:00
Stephane Nicoll 02132bd060 Infer resource hints for PropertySource#value
This commit adds a resource hints for the target(s) of a @PropertySource
declaration.

Closes gh-30376
2023-04-26 14:13:19 +02:00
Stephane Nicoll f6875b11ff Generate missing hints for custom PropertySource factories
Closes gh-30175
2023-04-26 13:40:48 +02:00
Sam Brannen 230840a41c Upgrade to JUnit 5.9.3 2023-04-26 10:26:56 +02:00
Sam Brannen 3b8a9b18f9 Upgrade to Gradle 8.1.1
Closes gh-30351
2023-04-25 19:51:54 +02:00
Sam Brannen d023b94a42 Introduce Environment.matchesProfiles() for profile expressions
Environment.acceptsProfiles(String...) was deprecated in 5.1 in
conjunction with gh-17063 which introduced a new
acceptsProfiles(Profiles) method to replace it. The deprecated method
only supports OR semantics; whereas, the new method supports profile
expressions. Thus, the goal was to encourage people to use the more
powerful profile expressions instead of the limited OR support with
profile names.

However, there are use cases where it is difficult (if not impossible)
to provide a Profiles instance, and there are use cases where it is
simply preferable to provide profile expressions directly as strings.

To address these issues, this commit introduces a new matchesProfiles()
method in Environment that accepts a var-args list of profile
expressions.

Closes gh-30206
2023-04-25 18:10:50 +02:00
Sam Brannen 47ba819f96 Polish Environment and StandardEnvironmentTests
See gh-30206
2023-04-25 18:10:39 +02:00
Sam Brannen ea4c64ebc4 Polish ProfilesParser internals 2023-04-25 15:08:03 +02:00
Sam Brannen de113f1d11 Reject null and empty SpEL expressions
Prior to gh-30325, supplying a null reference for a SpEL expression was
effectively equivalent to supplying the String "null" as the
expression. Consequently, evaluation of a null reference expression
always evaluated to a null reference. However, that was accidental
rather than by design.

Due to the introduction of the checkExpressionLength(String) method in
InternalSpelExpressionParser (in conjunction with gh-30325), an attempt
to evaluate a null reference as a SpEL expression now results in a
NullPointerException.

To address both of these issues,
TemplateAwareExpressionParser.parseExpression() and
SpelExpressionParser.parseRaw() now reject null and empty SpEL
expressions.

Closes gh-30371
2023-04-25 13:46:30 +02:00
Sam Brannen ca13b5cbca Polish SpelParserTests and TemplateExpressionParsingTests 2023-04-25 13:46:30 +02:00
James Yuzawa 5dacf50b9b Optimize MultiValueMap iteration operations
* use forEach and putIfAbsent to copy headers in DefaultClientRequestBuilder
* use forEach in ReactorClientHttpRequest and ReactorNetty2ClientHttpRequest
* circumvent ReadOnlyHttpHeaders.entrySet()
* ensure the fast path to LinkedCaseInsensitiveMap for forEach and putIfAbsent exists

Closes gh-29972
2023-04-25 09:57:26 +02:00
Arjen Poutsma beb23b5da6 Updated CI image JDK 2023-04-24 12:56:18 +02:00
Arjen Poutsma d07df24adc Updated sdkmanrc 2023-04-24 12:30:02 +02:00
Sam Brannen 2d1ddbb65c Polish contribution
See gh-30282
2023-04-20 16:18:08 +02:00
David Szigecsan af54ee9eba Polish ClassUtils and TypeUtils
Closes gh-30282
2023-04-20 15:51:25 +02:00
Stephane Nicoll 7a78092934 Merge pull request #30343 from divcon
* pr/30343:
  Polish contribution
  Use putIfAbsent in Netty5HeadersAdapter

Closes gh-30343
2023-04-20 15:48:52 +02:00
Stephane Nicoll ea0340f892 Polish contribution
See gh-30343
2023-04-20 15:46:12 +02:00
divcon f79d145b4c Use putIfAbsent in Netty5HeadersAdapter
See gh-30343
2023-04-20 15:45:56 +02:00
Stephane Nicoll 5deab3afd0 Merge pull request #30357 from izeye
* pr/30357:
  Polish

Closes gh-30357
2023-04-20 15:43:07 +02:00
Johnny Lim 66bd891a0b Polish
See gh-30357
2023-04-20 15:42:01 +02:00
Arjen Poutsma 685548b58b Merge pull request #30252 from luozhenyu:content-disposition
* gh-30252:
  Polish contribution
  Quote question marks in content-disposition
2023-04-18 15:33:52 +02:00
Arjen Poutsma 74d3268656 Polish contribution
This commit polishes an external contribution, ensuring that not just
spaces are encoded as underscores, and that underscores are encoded
as non-printable.

See gh-30252
2023-04-18 15:30:31 +02:00
luozhenyu 5a4a46af78 Quote question marks in content-disposition
This commit ensures that question marks are encoded, in accordance
with RFC 2047, section 4.2, rule (3).

Closes gh-30252
2023-04-18 14:47:57 +02:00
Brian Clozel a465b16f7c Set current version to 6.0.9-SNAPSHOT 2023-04-14 12:35:24 +02:00
Brian Clozel 5d1abfbc2d Update CI pipeline to 6.0.x branch 2023-04-13 12:46:06 +02:00
Sam Brannen be17c8d85f Disable variable assignment in SimpleEvaluationContext
This commit introduces infrastructure to differentiate between
programmatic setting of a variable in an EvaluationContext versus the
assignment of a variable within a SpEL expression using the assignment
operator (=). In addition, this commit disables variable assignment
within expressions when using the SimpleEvaluationContext.

Closes gh-30326
2023-04-13 09:40:23 +02:00
Sam Brannen b73f5fcac2 Limit SpEL expression length
This commit enforces a limit of the maximum size of a single SpEL
expression.

Closes gh-30325
2023-04-13 09:39:53 +02:00
Sam Brannen bc1511d667 Limit string concatenation in SpEL expressions
This commit introduces support for limiting the maximum length of a
string resulting from the concatenation operator (+) in SpEL
expressions.

Closes gh-30324
2023-04-13 09:39:32 +02:00
Sam Brannen db9b139cf0 Change max regex length in SpEL expressions to 1000
This commit changes the max regex length in SpEL expressions from 1024
to 1000 in order to consistently use "round" numbers for recently
introduced limits.

See gh-30265
2023-04-13 09:39:27 +02:00
rstoyanchev bd029b9218 Ensure RestClientResponseException is serializable
Closes gh-30224
2023-04-12 15:38:27 +01:00
rstoyanchev 5f2264816a Polishing contribution
Closes gh-30294
2023-04-12 15:22:57 +01:00
Yanming Zhou a8f31f5b9e Improve ProblemDetail equals and hashCode
Lazy computed title property should be taken into account

See gh-30294
2023-04-12 15:07:22 +01:00
Stephane Nicoll 90627b4345 Upgrade to Micrometer 1.10.6
Closes gh-30317
2023-04-12 14:45:54 +02:00
Juergen Hoeller 4acc71b282 Upgrade to Reactor 2022.0.6 and Netty 4.1.91
Includes Checkstyle 10.9.3 and Mockito 5.3.0

Closes gh-30316
2023-04-12 13:36:01 +02:00
kyuarl21 cd0379a67f Modified to slf4j2-impl in build.gradle
due to slf4j-api versions 1.7 x or earlier issue
2023-04-12 13:11:48 +02:00
Simon Baslé e262e98bab ConstructorResolver error hints about mixing indexed and named args
This commit adds a note to an exception in `ConstructorResolver`'s
`autowireConstructor` method hinting that attention should be paid to
cases that mix indexed arguments and named arguments. This is especially
when inheriting bean definitions in xml.

Closes gh-29976
Close gh-PR
2023-04-12 13:09:28 +02:00
SW 59c65fa940 Replace Collections.unmodifiableList(new ArrayList(..)) with List.copyOf() (#30166) 2023-04-12 13:07:20 +02:00
Stephane Nicoll 695601aa06 Merge pull request #30308 from izeye
* pr/30308:
  Add since tags to sameSite() and attribute() in CookieResultMatchersDsl

Closes gh-30308
2023-04-11 14:57:13 +02:00
Johnny Lim 8f5a1fe7e2 Add since tags to sameSite() and attribute() in CookieResultMatchersDsl
See gh-30308
2023-04-11 14:56:55 +02:00
rstoyanchev 073226d792 Polishing contribution
Closes gh-30120
2023-04-11 11:43:24 +01:00
Aleksandrs Jansons 1abe155663 Ensure WebSocket disconnect msg reaches the client
In some application setups, the WebSocket server does not transmit
the disconnect message to the client, so that the client has no idea
that the established connection has been terminated.

This issue arises when the application uses SimpleBrokerMessageHandler
and the error handler is set to the instance of
StompSubProtocolErrorHandler or an extended class that does not
override the handleErrorMessageToClient method.

The commit fixes disconnect message population so that
`java.lang.IllegalArgumentException: No StompHeaderAccessor` exception
is not thrown in the handleErrorMessageToClient method in
StompSubProtocolErrorHandler class.

See gh-30120
2023-04-11 11:43:23 +01:00
divcon 33ef9107e0 Minor refactoring in PayloadMethodArgumentResolver
Closes gh-30168
2023-04-11 11:36:41 +01:00
rstoyanchev 8463eade33 Polishing contribution
Closes gh-30192
2023-04-10 21:24:25 +01:00
James Yuzawa e77faf7484 Improve performance of canRead() in HttpMessageReader's
Use MimeType.WILDCARD_TYPE for faster String.equals().
Move cheaper checks to the front of the canRead implementations.

See gh-30192
2023-03-25 12:21:10 -04:00
Sam Brannen b23cc01cb7 Revise "Ignore nonexistent default-destroy-method in XML config"
This commit revises the fix in c811428512.

Closes gh-30301
2023-04-07 18:48:52 +02:00
Brian Clozel 01f97887ea Improve WebClient observations handling of CANCEL signal
Prior to this commit, `WebClient` observations would be recorded as
aborted (with tags "outcome":"UNKNOWN", "status":"CLIENT_ERROR")
for use cases like this:

```
Flux<String> result = client.get()
    .uri("/path")
    .retrieve()
    .bodyToFlux(String.class)
    .take(1);
```

This is due to operators like `take` or `next` that consume *some*
`onNext` signals and then cancels the subscription before completion.
This means the subscriber is only partially interested in the response
and we should not count this as a client error.

This commit ensures that observations are only recorded as aborted if
the response was not published at the time the CANCEL signal was
received.

The code snippet above will now publish observations with
"outcome":"SUCCESS" and "status":"200" tags, for example.

Closes gh-30070
2023-04-07 16:37:09 +02:00
Sam Brannen cef597bedd Update copyright headers 2023-04-07 14:24:22 +02:00
Sam Brannen 01fabfe66d Suppress warnings in tests 2023-04-07 14:23:55 +02:00
Sam Brannen c811428512 Ignore nonexistent default-destroy-method in XML config
Prior to this commit, DisposableBeanAdapter attempted to invoke a
configured default-destroy-method on every bean, including beans that
do not declare the named destroy method, resulting in a
NullPointerException being thrown and logged at WARN level.

This commit addresses this by effectively ignoring any nonexistent
destroy method.

Closes gh-30301
2023-04-07 13:58:49 +02:00
Sébastien Deleuze b5b115e52c Fix SSE with indenting serializer in WebMvc.fn
This commit ensures that HTTP headers like "text/event-stream"
are correctly forwarded to the converter used in
SseServerResponse for proper pretty print handling.

Close gh-30277
2023-04-07 11:25:47 +02:00
Sam Brannen 310344cf61 Increase max regex length in SpEL expressions
This commit increases the max regex length in SpEL expressions from 256
to 1024 in order to support use cases where a regex may be rather long
without necessarily increasing the complexity of the regex.

Closes gh-30265
2023-04-06 17:53:03 +02:00
Sam Brannen 2bac371c5b Improve Javadoc for ObjectUtils.nullSafeConciseToString() 2023-04-06 17:35:50 +02:00
Arjen Poutsma cef9166833 Encode IPV6 Zone IDs in ReactorServerHttpRequest
This commit ensures that the zone id in the ReactorServerHttpRequest is
properly encoded.

Closes gh-30188
2023-04-06 11:32:44 +02:00
Simon Baslé d6460e0d57 Add Cookie attributes + SameSite CookieResultMatchers in MockMvc
This commit adds assertions to MockMvc's CookieresultMatchers:
 - `attribute` for arbitrary attributes
 - `sameSite` for the SameSite well-known attribute

Note that the `sameSite` methods delegate to their `attribute`
counterparts. Note also that Jakarta's `Cookie#getAttribute` method is
case-insensitive, which is reflected in the documentation of the
`attribute` assertion method and the tests.

Closes gh-30285
2023-04-05 17:02:38 +02:00
Sam Brannen 842490beeb Add tests for corner cases
See gh-30290
See gh-30286
2023-04-05 15:05:25 +02:00
Sam Brannen e746230de6 Introduce ObjectUtils.nullSafeConciseToString()
ObjectUtils.nullSafeToString(Object) exists for generating a string
representation of various objects in a "null-safe" manner, including
support for object graphs, collections, etc.

However, there are times when we would like to generate a "concise",
null-safe string representation that does not include an entire object
graph (or potentially a collection of object graphs).

This commit introduces ObjectUtils.nullSafeConciseToString(Object) to
address this need and makes use of the new feature in FieldError and
ConversionFailedException.

Closes gh-30286
2023-04-05 14:13:28 +02:00
Sam Brannen 8161316b1d Introduce StringUtils.truncate()
StringUtils.truncate() serves as central, consistent way for truncating
strings used in log messages and exception failure messages, for
immediate use in LogFormatUtils and ObjectUtils.

See gh-30286
Closes gh-30290
2023-04-05 13:50:13 +02:00
Krzysztof Krasoń 1734deca1e Refactor AssertJ assertions into more idiomatic ones
This commit refactors some AssertJ assertions into more idiomatic and
readable ones. Using the dedicated assertion instead of a generic one
will produce more meaningful error messages. 

For instance, consider collection size:
```
// expected: 5 but was: 2
assertThat(collection.size()).equals(5);
// Expected size: 5 but was: 2 in: [1, 2]
assertThat(collection).hasSize(5);
```

Closes gh-30104
2023-04-04 17:34:07 +02:00
Justin Tay dd97ee4e99 Support SameSite cookie attribute in MockMvcHttpConnector
Closes gh-30264
2023-04-04 16:57:21 +02:00
Simon Baslé 90b0f451f0 Add a couple missing java.time types to StatementCreatorUtils
This commit adds mapping for two types from the `java.time` package,
complementing the types that are already translatable to Sql types
TIME, DATE and TIMESTAMP:
 - `OffsetTime` maps to a `TIME_WITH_TIMEZONE`
 - `OffsetDateTime` maps to a `TIMESTAMP_WITH_TIMEZONE`

This is in accordance with the B.4 table provided in the JDBC 4.2
specification.

When preparing statements, these `java.time` types use the `setObject`
method. Tests covering the 5 `java.time` classes have also been added.

See gh-28778
See gh-28527
Closes gh-30123
2023-04-04 16:51:24 +02:00
Sam Brannen 9fb61c57ae Sync MockCookie implementations
See gh-30263
2023-04-04 16:32:30 +02:00
Sam Brannen f9cb0eba87 Update Javadoc regarding Servlet 6 baseline for mocks 2023-04-04 16:29:51 +02:00
Sam Brannen d1d2d5943e Polish contribution
See gh-30263
2023-04-04 16:29:03 +02:00
Justin Tay 281736f14e Update MockCookie to use Servlet 6.0 APIs and semantics for "attributes"
Closes gh-30263
2023-04-04 16:13:37 +02:00
Sam Brannen 6bfc70b61e Polishing 2023-04-04 16:02:54 +02:00
Simon Baslé 95883b9eb7 Rename MockMVC matcher methods to prevent regression in user tests
This commit changes the name of two recently introduced methods in the
`MockRestRequestMatchers` class for header and queryParam. These have
been found to cause false negatives in user tests, due to the new
overload taking precedence in some cases.

Namely, using a `Matcher` factory method which can apply to both `List`
and `String` will cause the compiler to select the newest list overload,
by instantiating a `Matcher<Object>`.

This can cause false negatives in user tests, failing tests that used
to pass because the Matcher previously applied to the first String in
the header or queryParam value list. For instance, `equalsTo("a")`.

The new overloads are recent enough and this has enough potential to
cause an arbitrary number of user tests to fail that we break the API
to eliminate the ambiguity, by renaming the methods with a `*List`
suffix.

Closes gh-30220
Closes gh-30238
See gh-29953
See gh-28660
2023-04-04 15:06:14 +02:00
Arjen Poutsma f0eb43a6af Merge pull request #30157 from srivatsa-cfp:main
* gh-30157:
  Add non-null assertions in DefaultServerResponseBuilder
2023-04-04 12:42:39 +02:00
Vatsa 6c8ebc7f7e Add non-null assertions in DefaultServerResponseBuilder
This commit adds various non-null assertions to
DefaultServerResponseBuilder, in both Spring MVC and WebFlux.

Closes gh-30157
2023-04-04 12:36:46 +02:00
Sam Brannen 69c8f8e9c7 Update copyright headers 2023-04-03 16:45:49 +02:00
Sam Brannen 02f2d94f57 Polish contribution
See gh-30271
2023-04-03 16:45:49 +02:00
wizard 7fcbc869a6 Specify initial capacity when creating ArrayList in SpringFactoriesLoader
Closes gh-30271
2023-04-03 16:42:00 +02:00
Sam Brannen 4eed2ced74 Polishing 2023-04-03 14:42:38 +02:00
Sébastien Deleuze 98f1287f3a Fix HttpServiceMethod support for suspending functions
This commit fixes nested type handling for suspending
functions in HttpServiceMethod.

Closes gh-30266
2023-04-03 10:30:49 +02:00
Brian Clozel 8234fa2a13 Fix JDK20 env variable declaration in CI pipeline
See gh-30185
2023-04-03 09:43:37 +02:00
Filip Blondeel 8b9d2e3f95 Fix incorrect jakarta.inject artifact version
Closes gh-30270
2023-04-03 09:36:22 +02:00
hongxue.zou 534d1cd35b Polishing
This commit includes a null-safety fix in
HttpComponentsHeadersAdapter.

Closes gh-30267
2023-04-03 09:04:39 +02:00
Sam Brannen ca545ac3d4 Upgrade dependencies 2023-04-02 19:20:49 +02:00
Sam Brannen ca7c2779bf Suppress warnings displayed in Gradle build for test source code 2023-04-01 18:36:56 +02:00
Sam Brannen 4a90257a84 Upgrade to Gradle 8
This commit upgrades the build to use Gradle 8.0.2 and Dokka 1.8.10.

Closes gh-30000
2023-04-01 18:36:28 +02:00
Sam Brannen 49a4ed2ffa Improve Javadoc for @PropertySource 2023-04-01 18:15:19 +02:00
Sam Brannen 717d03a29d Fix grammar 2023-04-01 18:15:19 +02:00
1993heqiang 2b427efe7f Fix PathVariable reference documentation code snippets
Closes gh-30243
2023-03-31 15:51:56 +02:00
Sam Brannen 3632bea51e Polishing 2023-03-31 13:52:05 +02:00
Brian Clozel 1efa162cf0 WebClient defaultStatusHandler do not apply to exchangeTo*
This commit documents the fact that default status handlers configured
on the `WebClient` are not applied to `exchangeTo*` methods as those
variants give full access to the client response.

Applying them here would restrict the ability to adapt the behavior
depending on the HTTP response status.

Closes gh-30059
2023-03-31 11:19:40 +02:00
Harry Yang a8b7a5e037 Refine initRequestBuilder in DefaultWebClient
Closes gh-30254
2023-03-31 10:53:51 +02:00
Sébastien Deleuze 807325916f Add class hints for Jackson annotations on fields and methods
Before this commit, only class level annotations were
processed.

Closes gh-30208
2023-03-31 10:39:18 +02:00
Brian Clozel d451d6adcc Ensure that client responses are observed when filters fail
Prior to this commit, an error thrown by a `ExchangeFilterFunction`
configured on a `WebClient` instance would be recorded as such by the
client observation, but the response details would be missing from the
observation.
All filter functions and the exchange function (performing the HTTP
call) would be merged into a single `ExchangeFunction`; this instance
was instrumented and osberved. As a result, the instrumentation would
only get the error signal returned by the filter function and would not
see the HTTP response even if it was received. This means that the
recorded observation would not have the relevant information for the
HTTP status.

This commit ensures that between the configured `ExchangeFilterFunction`
and the `ExchangeFunction`, an instrumentation `ExchangeFilterFunction`
is inserted. This allows to set the client response to the observation
context, even if a later error signal is thrown by a filter function.

Note that with this change, an error signal sent by a filter function
will be still recorded in the observation.

See gh-30059
2023-03-30 20:04:48 +02:00
Juergen Hoeller 8fca258207 Propagate HttpStreamResetException itself if cause is null
Closes gh-30245
2023-03-30 19:22:30 +02:00
Sébastien Deleuze d126b99c91 Refine generic type management in AbstractMessageWriterResultHandler
This commit updates AbstractMessageWriterResultHandler#writeBody in
order to use the declared bodyParameter instead of
ResolvableType.forInstance(body) when the former has unresolvable
generics.

Closes gh-30214
2023-03-30 18:07:01 +02:00
Sébastien Deleuze c5f0f7bb11 Refine DisposableBeanAdapter method discovery for native
After this commit, DisposableBeanAdapter can find destruction related methods
even when hints are just specified at interface level, which is typically the
case when a bean is exposed via one of its interfaces.

Closes gh-29545
2023-03-30 15:28:50 +02:00
Brian Clozel 3a36d51473 Polish
See gh-29246
2023-03-30 14:49:11 +02:00
Brian Clozel fe6589d5af Add runtime hint test for inherited destroy methods
This commit ensures that init/destroy methods that are provided as
default methods from interfaces are properly covered by runtime hints at
runtime.

Closes gh-29246
2023-03-30 14:42:19 +02:00
Brian Clozel b374824319 Contribute introspection hints on registered beans
Prior to this commit, reflection hints registered for beans was
selectively applied to only consider the methods that we'll actually
need reflection on at runtime. This would rely on an undocumented
behavior of GraalVM Native where calling `getDeclaredMethods` on a type
would only return known metadata at runtime, ignoring the ones that were
not registered during native compilation.

As of oracle/graal#5171, this behavior is now fixed in GraalVM and
aligns with the JVM behavior: all methods will be returned. This means
that if during native compilation, introspection was not registered for
the type a new `MissingReflectionMetadataException` will be raised.

As a follow up of #29205, this commit contributes the "introspection on
declared method" reflection hint for all registered beans.

Closes gh-29246
2023-03-29 21:16:59 +02:00
Juergen Hoeller 7e905e3e00 Use JdkDynamicAopProxy class loader instead of JDK bootstrap/platform loader
Closes gh-30115
2023-03-29 13:46:29 +02:00
Juergen Hoeller 491ae1e3be Use MethodInvocationInfo class loader in case of JDK platform loader as well
Closes gh-30210
2023-03-29 13:46:18 +02:00
Brian Clozel 66cdf43b56 Polish
Closes gh-30223
2023-03-29 10:55:48 +02:00
James Yuzawa 2ba206f8ba Order metric labels in ObservationConventions
See gh-30223
2023-03-29 10:55:34 +02:00
James Yuzawa 5ba6944145 Remove String.formatter from DefaultServerRequestObservationConvention
Closes gh-30218
2023-03-28 20:24:55 +02:00
Arjen Poutsma 5609e67100 Improve Javadoc of MultipartBodyBuilder
This commit improves the Javadoc of MultipartBodyBuilder, to make it
clear that it is intended for multipart/form-data.

See gh-30179
2023-03-28 14:04:09 +02:00
Juergen Hoeller b2be07c73c Polishing 2023-03-28 13:26:29 +02:00
Juergen Hoeller ce2689eead Use MethodInvocationInfo class loader in case of core JDK interface type
Closes gh-30210
2023-03-28 13:20:49 +02:00
Johnny Lim 2184d4e80e Fix incomplete assertions
Closes gh-30209
2023-03-28 10:56:41 +02:00
Ed .d 50d01ce405 feat: Add support for Vavr's Try monad to trigger transaction rollbacks
Updated the Spring Framework documentation to include an example of using Vavr's Try monad to trigger transaction rollbacks when a @Transactional-annotated method returns a Failure. The modified documentation demonstrates how to use Try in a transactional method and how to check if an exception has been wrapped inside a Try.Failure instance. Additionally, a link to the official Vavr documentation was added to provide more information on the Try method.
2023-03-28 10:38:03 +02:00
Sam Brannen 0c0cda9815 Polish contribution
See gh-30189
2023-03-27 19:31:35 +02:00
Harry Yang 47aca90c58 Optimize array creation in SpEL ConstructorReference
- Create primitive arrays directly instead of using Array#newInstance.

- Replace if-else blocks with enhanced switch statement.

Closes gh-30189
2023-03-27 19:31:35 +02:00
Sam Brannen ddd6b123bb Polish TypeCode 2023-03-27 19:31:35 +02:00
heqiang ec270c7135 Use diamond operator in examples in reference manual
Closes gh-30204
2023-03-27 18:27:29 +02:00
Sébastien Deleuze 7cc72ddf7a Add HttpMethod reflection hint to ObjectToObjectConverterRuntimeHints
This commit adds a reflection hint for HttpMethod#valueOf in order
to be able to support conversion required for Spring Boot
configuration properties binding as described in
https://github.com/spring-projects/spring-boot/issues/34483.

Closes gh-30201
2023-03-27 15:13:15 +02:00
Sam Brannen 5f0ee2e4dd Polish ConstructorReference 2023-03-25 17:40:00 +01:00
Johnny Lim 43031509c8 Update versions in Javadoc
Closes gh-30170
2023-03-25 17:13:36 +01:00
ghostg00 e66c80667f Fix example in Javadoc for @EnableWebSocket
The `echoWebSocketHandler()` method is not defined in the
`WebSocketConfigurer` interface and should therefore be annotated with
`@Bean` instead of `@Override`.

Closes gh-30183
2023-03-24 17:54:33 +01:00
Sam Brannen 84714fbae9 Allow JDK 20 builds to pass by using legacy locale data
After setting up a JDK 20 CI build pipeline, numerous tests involving
date/time parsing and formatting began to fail [0]. (The failing JPA
tests are not specific to JDK 20.)

For example, we encounter visually confusing assertion failures such as
the following.

org.opentest4j.AssertionFailedError:
	expected: "12:00 PM"
	but was:  "12:00 PM"

The expected string contains a normal space (which has always been the
case prior to JDK 20); whereas, the actual string now contains a narrow
non-breaking space.

The cause of this is mentioned in the JDK 20 Release Notes [1] as "NBSP
prefixed to a, instead of a normal space". Note, however, that the
links for the first two bullet points in that section are mixed up.
"NBSP prefixed to a, instead of a normal space" should point to [2].
Furthermore, the new whitespace character is not a non-breaking space
(NBSP) but rather a narrow non-breaking space (NNBSP). In addition, the
second bullet point should technically read "NNBSP prefixed to `a`,
instead of a normal space" -- even though `a` provides limited value to
most readers.

The downside for the Java community is that this constitutes a breaking
change for parsing and formatting date/time values that include "AM"
and "PM" units (any may potentially apply to other date/time
parsing/formatting scenarios). In Spring Framework's test suite we have
witnessed this in conjunction with Spring's @DateTimeFormat and
DateTimeFormatterFactory infrastructure as well as with Google's
Gson-to-JSON support.

A colleague who works at Oracle graciously informed me that one can use
"legacy locale data" by supplying `-Djava.locale.providers=COMPAT` as a
JVM argument, noting however that this option limits some newer
functionalities (but without enumerating which new functionalities one
might be missing when using this option).

In any case, this commit adds that JVM argument to our Gradle toolchain
builds so that our test suite passes on JDK 20, and we will continue to
investigate further options for our builds and for our users.

Note, however, that one must manually configure the
`-Djava.locale.providers=COMPAT` JVM argument when running affected
tests within an IDE.

See gh-30185

[0] https://ge.spring.io/s/kmiq2bz2afafs/tests/overview?outcome=failed
[1] https://jdk.java.net/20/release-notes#JDK-8284840
[2] https://unicode-org.atlassian.net/browse/CLDR-14032
2023-03-24 16:08:38 +01:00
Sam Brannen 0ca02ce677 Disable affected tests on Java 18+/19+
See gh-30185
2023-03-24 16:05:40 +01:00
Sam Brannen db29b65399 Polishing 2023-03-24 16:05:40 +01:00
Sam Brannen 18adf905a8 Polishing 2023-03-23 17:04:41 +01:00
Sam Brannen ce9a72f95c Update copyright headers 2023-03-23 16:56:20 +01:00
Sam Brannen dfb4a951ae Suppress deprecation warnings instead of deprecating tests 2023-03-23 16:55:12 +01:00
Brian Clozel d9776941bf Update Java version for compatibility tests in CI
This commit configures Java 20 for compatibility tests in our CI,
replacing Java 19.
2023-03-23 15:47:06 +01:00
Giuseppe 24b359d519 Handle all exceptions for stored proc out param retrieval in SharedEntityManagerCreator
Prior to this commit, the EntityManager was not closed in
SharedEntityManagerCreator.DeferredQueryInvocationHandler's
invoke(Object, Method, Object[]) method if an invocation of
getOutputParameterValue(*) threw an exception other than
IllegalArgumentException, which could lead to a connection leak.

This commit addresses this by catching RuntimeException instead of
IllegalArgumentException.

Closes gh-30161
2023-03-22 15:39:53 +01:00
Arjen Poutsma ae70bf7c38 Merge pull request #30138 from yuzawa-san:LiteralPathElement-string-equals
* gh-30138:
  Polish external contribution
  Use String.equals() in LiteralPathElement
2023-03-22 12:13:45 +01:00
Arjen Poutsma c68e986b75 Polish external contribution
This commit removes the text char[] in favor of the text String
introduced through the PR.

Closes gh-30138
2023-03-22 12:10:18 +01:00
James Yuzawa 2bc1aa7827 Use String.equals() in LiteralPathElement 2023-03-22 12:10:18 +01:00
Kukri 570d21ebbd Fix anchor in link to "Web on Reactive Stack" chapter
Closes gh-30158
2023-03-22 11:30:35 +01:00
James Yuzawa 800b13492b Optimize some iterations in BodyExtractor and BodyInserter
This commit turns some stream-based iterations back into simpler
enhanced for loops.

For simple use cases like these, where the stream API is merely used to
map/filter + collect to a List, a for loop is more efficient.
This is especially true for small collections like the ones we deal
with in BodyInserters/BodyExtractors here (in the order of 50ns/op vs
5ns/op). These cases are also simple enough that they don't lose in
readability after the conversion.

Closes gh-30136
2023-03-22 11:04:54 +01:00
Сергей Цыпанов 4e896c8125 Use InputStream.readAllBytes() in FileCopyUtils.copyToByteArray()
InputStream.readAllBytes() allows us to avoid the creation of an
intermediate ByteArrayOutputStream and is likely to perform better.

Closes gh-30155
2023-03-21 15:37:26 +01:00
Arjen Poutsma 9421fe1d75 Merge pull request #30139 from yuzawa-san:cache-reactor-request-methods
* gh-30139:
  Cache ServerHttpRequest::getMethod in AbstractServerHttpRequest
  cache reactor request methods
2023-03-21 11:36:11 +01:00
Arjen Poutsma 37a4e84450 Cache ServerHttpRequest::getMethod in AbstractServerHttpRequest
This commit ensures that the HttpMethod, exposed through
ServerHttpRequest::getMethod, is cached in AbstractServerHttpRequest so
that potentially expensive HTTP method lookups are only done once.

Closes gh-30139
2023-03-21 11:29:32 +01:00
James Yuzawa c27a5687dc cache reactor request methods 2023-03-21 09:39:04 +01:00
Spring Builds 4e8162c6dd Next development version (v6.0.8-SNAPSHOT) 2023-03-20 09:53:24 +00:00
rstoyanchev 202fa5cdb3 Polishing and minor refactoring in HandlerMappingIntrospector
Closes gh-30127
2023-03-20 08:36:40 +00:00
Sam Brannen 8010de8b63 Improve diagnostics in SpEL for matches operator
Supplying a large regular expression to the `matches` operator in a
SpEL expression can result in errors that are not very helpful to the
user.

This commit improves the diagnostics in SpEL for the `matches` operator
by throwing a SpelEvaluationException with a meaningful error message
to better assist the user.

Closes gh-30144
2023-03-19 23:57:55 +01:00
Sam Brannen 5529294ec9 Improve diagnostics in SpEL for repeated text
Attempting to create repeated text in a SpEL expression using the
repeat operator can result in errors that are not very helpful to the
user.

This commit improves the diagnostics in SpEL for the repeat operator by
throwing a SpelEvaluationException with a meaningful error message in
order to better assist the user.

Closes gh-30142
2023-03-19 23:57:55 +01:00
Sam Brannen 935c29e3dd Increase scope of regex pattern cache for the SpEL matches operator
Prior to this commit, the pattern cache for the SpEL `matches` operator
only applied to expressions such as the following where the same
`matches` operator is invoked multiple times with different input:

  "map.keySet().?[#this matches '.+xyz']"

The pattern cache did not apply to expressions such as the following
where the same pattern ('.+xyz') is used in multiple `matches`
operations:

  "foo matches '.+xyz' AND bar matches '.+xyz'"

This commit addresses this by moving the instance of the pattern cache
map from OperatorMatches to InternalSpelExpressionParser so that the
cache can be reused for all `matches` operations for the given parser.

Closes gh-30140
2023-03-19 23:57:55 +01:00
Sam Brannen 4a3518b4d6 Polishing 2023-03-19 23:55:45 +01:00
Sam Brannen dd4a34778b Stop printing to System.out in SpEL tests 2023-03-19 23:55:36 +01:00
Brian Clozel 46bd6add15 Mention JAR signing key in SECURITY.md
This commit adds a link to the now published information on spring.io
about the GPG key used to sign the Spring artifacts published on Maven
Central.

Closes gh-23434
2023-03-17 22:32:33 +01:00
Juergen Hoeller 19384ac8ad Polishing 2023-03-17 17:37:23 +01:00
Juergen Hoeller a0358a4650 Upgrade to Reactor 2022.0.5 and Netty 4.1.90
Includes Checkstyle 10.9.1 and Mockito 5.2.0

Closes gh-30133
2023-03-17 17:36:51 +01:00
Juergen Hoeller 88bc504625 Skip validation constraint hint inference if no provider available
Closes gh-30130
2023-03-17 17:36:31 +01:00
Brian Clozel 3af8efbdc7 Add anchor rewrites in the reference documentation
This commit adds an `anchor-rewrite.properties` file in the reference
documentation sources. This allows to redirect from one anchor to
another when they are renamed.

This also populates the file with anchors that were renamed in the AOT
section of the documentation.

Closes gh-30132
2023-03-17 17:28:12 +01:00
Brian Clozel 2e08a07d7c Add a list of produced observations
This commit adds a list observations produced by Spring Framework in the
reference documentation.

Closes gh-30060
2023-03-17 17:27:11 +01:00
Sam Brannen df4e7d1929 Polishing 2023-03-15 15:08:47 +01:00
Brian Clozel 2a5eab4b89 Ignore quality factor when filtering out "*/*"
Prior to this commit, the `RequestedContentTypeResolverBuilder` would
create a `RequestedContentTypeResolver` that internally delegates to a
list of resolvers. Each resolver would either return the list of
requested media types, or a singleton list with the "*/*" media type; in
this case this signals that the resolver cannot find a specific media
type requested and that we should continue with the next resolver in the
list.

Media Types returned by resolvers can contain parameters, such as the
quality factor. If the HTTP client requests "*/*;q=0.8", the
`HeaderContentTypeResolver` will return this as a singleton list. While
this has been resolved from the request, such a media type should not be
selected over other media types that could be returned by other
resolvers.

This commit changes the `RequestedContentTypeResolverBuilder` so that it
does not select "*/*;q=0.8" as the requested media type, but instead
continues delegating to other resolvers in the list. This means we need
to remove the quality factor before comparing it to the "*/*" for
equality check.

Fixes gh-29915
2023-03-15 10:16:27 +01:00
Sam Brannen 1de36abc07 Revise contribution
See gh-25316
2023-03-14 17:28:12 +01:00
mrcoffee77 97b5af8a55 Ensure methods declared in Object can be invoked on a JDK proxy in SpEL
This commit ensures that methods declared in java.lang.Object (such as
toString() can be invoked on a JDK proxy instance in a SpEL expression.

Closes gh-25316
2023-03-14 17:19:06 +01:00
Sam Brannen 4751769a7c Polishing 2023-03-14 17:18:43 +01:00
Arjen Poutsma 1f8e9f5c55 Support Windows path in ContentDisposition::parse
This commit makes sure that ContentDisposition::parse supports Windows
path with a backslash.

Closes gh-30111
2023-03-14 14:19:33 +01:00
rstoyanchev c3ce847871 Polishing contribution
Closes gh-30092
2023-03-14 11:10:02 +00:00
James Yuzawa cd8955fa72 Remove extra copy in WebClient headers/cookies
See gh-30092
2023-03-14 10:49:53 +00:00
rstoyanchev d18bcb3f3d Raise MethodArgumentNotValidException consistently
Closes gh-30100
2023-03-14 09:08:57 +00:00
Sam Brannen e17f5c50a8 Update copyright headers 2023-03-13 21:53:40 +01:00
Sam Brannen 00be19c647 Consistently declare Object::equals argument as @Nullable 2023-03-13 21:43:21 +01:00
Sam Brannen a6dab10309 Update code regarding null-safety semantics
See gh-30083
2023-03-13 21:19:46 +01:00
Sam Brannen b617e16d8d Polishing 2023-03-13 21:16:02 +01:00
Sam Brannen 9cf7b0e230 Polishing 2023-03-12 18:38:50 +01:00
Juergen Hoeller 3431b2330a Avoid unnecessary parameter name inspection for factory method type check
Closes gh-30103
See gh-29612
2023-03-10 19:19:19 +01:00
Sam Brannen 99e54fec3a Ensure all packages declare package-info.java with null-safety annotations
This commit picks up where the two previous commits left off.

Specifically, this commit:

- Removes the "severity=warning" configuration to ensure that violations
  actually fail the build.
- Fixes regular expressions for suppressions by matching forward
  slashes using `[\\/]` instead of `\/`.
- Moves the configuration for newly introduced checks to locations in
  checkstyle.xml that align with the existing organization of that file.
- Renames the IDs for RegexpSinglelineJava checks from
  javaDocPackageNonNullApiAnnotation/javaDocPackageNonNullFieldsAnnotation
  to packageLevelNonNullApiAnnotation/packageLevelNonNullFieldsAnnotation,
  respectively, since these checks are not related to Javadoc.
- Simplifies the null-safety annotation checks to match against
  imported annotation types, which enforces consistency across
  package-info.java files for the annotation declarations.
- Simplifies the RegEx for JavadocPackage suppressions to only exclude
  packages not under src/main/java (vs src/main) and those in the
  framework-docs module.
- Consistently suppresses all checks for the `asm`, `cglib`, `objenesis`,
  and `javapoet` packages in spring-core.
- Adds explicit suppressions for null-safety annotations for the `lang`
  package in spring-core.
- Adds explicit suppressions for null-safety annotations for the
  `org.aopalliance` package in spring-aop.
- Revises the RegEx for null-safety annotation suppressions to only
  exclude package-info.java files not under src/main/java and
  additionally to exclude package-info.java files in the framework-docs
  module as well as those in the spring-context-indexer,
  spring-instrument, and spring-jcl modules.
- Adds all missing package-info.java files.
- Adds null-safety annotations to package-info.java files where
  appropriate.

Closes gh-30069
2023-03-10 17:33:52 +01:00
Ed .d 7e32f504b0 Configure Checkstyle to require package-level null-safety annotations
This commit updates the project's checkstyle configuration to check
that package-info.java files contain the @NonNullApi and @NonNullFields
null-safety annotations.

See gh-30069
2023-03-10 16:12:07 +01:00
Ed .d 268e3fec99 Configure Checkstyle to require package-info.java files
This commit updates the project's checkstyle configuration to require
the existence of a package-info.java file for all packages within the
src/main directory while excluding the framework-docs module and
certain packages inside the spring-core module. A missing
package-info.java file will result in emitting a warning.

See gh-30069
2023-03-10 16:09:17 +01:00
Simon Baslé 9b50c0d590 Ensure reactive transaction rollback on commit error
This change fixes a situation where error handling was skipped during
`processCommit()` in case the `doCommit()` failed. The error handling
was set up via an `onErrorResume` operator that was nested inside a
`then(...)`, applied to an inner `Mono.empty()`. As a consequence,
it would never receive an error signal (effectively decoupling the
onErrorResume from the main chain).

This change simply moves the error handling back one level up. It also
simplifies the `doCommit` code a bit by getting rid of the steps that
artificially introduce a `Mono<Object>` return type, which is not really
needed.

A pre-existing test was missing the fact that the rollback didn't occur,
which is now fixed. Another dedicated test is introduced building upon
the `ReactiveTestTransactionManager` class.

Closes gh-30096
2023-03-10 12:23:44 +01:00
Sam Brannen 2e5d0470dc Polishing 2023-03-09 14:19:59 +01:00
liupeng d2868f5dd0 Use Set to track ignored properties in BeanUtils.copyProperties()
Closes gh-30088
2023-03-09 14:11:22 +01:00
Abel Salgado Romero 1acbc97b16 Fix minor spacings in webflux docs
Closes gh-30078
2023-03-09 11:58:16 +01:00
Arjen Poutsma e88ec06a36 Disable flaky unit test for undertow 2023-03-09 10:03:07 +01:00
Juergen Hoeller 2302358606 Upgrade to Log4J 2.20, Tomcat 10.1.7, Jetty 11.0.14, Undertow 2.3.4 2023-03-08 16:53:43 +01:00
Juergen Hoeller d213522dfc Polishing 2023-03-08 16:49:32 +01:00
Juergen Hoeller 95710646d1 Pass pre-determined merged bean definition into InstanceSupplier (for inner beans)
Replaces useless protected obtainFromSupplier method with obtainInstanceFromSupplier.
Moves InstanceSupplier handling to appropriate subclass (DefaultListableBeanFactory).
BeanInstanceSupplier throws BeanInstantiationException instead of BeanCreationException.

Closes gh-29803
2023-03-08 16:49:25 +01:00
Brian Clozel 6cd67412cc Avoid lock contention in CaffeineCacheManager
Prior to this commit, using a dynamic `CaffeineCacheManager` would rely
on `ConcurrentHashMap#computeIfAbsent` for retrieving and creating cache
instances as needed. It turns out that using this method concurrently
can cause lock contention even when all known cache instances are
instantiated.

This commit avoids using this method if the cache instance already
exists and avoid storing `null` entries in the map. This change reduces
lock contention and the overall HashMap size in the non-dynamic case.

Fixes gh-30066
2023-03-08 16:23:32 +01:00
Arjen Poutsma e427ea8086 Merge pull request #30046 from srivatsa-cfp:main
* gh-30046:
  Add non-null assertions in DefaultServerRequestBuilder
2023-03-08 15:36:37 +01:00
Vatsa d8fbd35467 Add non-null assertions in DefaultServerRequestBuilder
This commit adds various non-null assertions to
DefaultServerRequestBuilder, in both Spring MVC and WebFlux.

Closes gh-30046
2023-03-08 12:50:02 +01:00
Sam Brannen a31cfba992 Apply "instanceof pattern matching" in remainder of spring-websocket module
See gh-30067
2023-03-07 19:18:25 +01:00
Sébastien Deleuze 14973fd6f3 Unset reproducibleFileOrder and preserveFileTimestamps Gradle flags
This commit reverts the changes done via 50a0094a47
in order to ensure consistency across modules (those changes were specific to
spring-core).

Reproducible builds are likely desirable, but it is a complex topic
(see gradle/gradle#14819) that needs to be addressed portfolio wide, with
a better control than what Gradle currently allows (for example to allow
using EPOCH instead of the current 1980 date).

Basic tests did not show an obvious impact on testing avoidance with modern
Gradle versions.

Closes gh-29633
2023-03-07 18:54:19 +01:00
Sam Brannen ffe7ec4a99 Apply "instanceof pattern matching" in remainder of spring-webmvc module
See gh-30067
2023-03-07 18:23:15 +01:00
Sam Brannen 9b811a01f6 Polishing 2023-03-07 15:20:10 +01:00
Sam Brannen 2b23d1693d Remove unused service package 2023-03-07 15:20:10 +01:00
Sam Brannen 9011ce9c68 Apply "instanceof pattern matching" in remainder of spring-web module
See gh-30067
2023-03-07 15:20:10 +01:00
Sam Brannen 29fe0a3c6a Update TODO for linking to JUnit 5 docs
See gh-27497
2023-03-07 13:28:25 +01:00
Sébastien Deleuze c20efba45c End javadoc generated AOT with a period consistently
Closes gh-29357
2023-03-07 11:20:37 +01:00
Sam Brannen fa95bf4dc1 Apply "instanceof pattern matching" in remainder of spring-test module
See gh-30067
2023-03-06 17:49:26 +01:00
Sam Brannen eea000d034 Apply "instanceof pattern matching" in remainder of spring-r2dbc module
See gh-30067
2023-03-06 17:21:28 +01:00
Sam Brannen a6338fcc43 Update copyright headers 2023-03-06 17:20:42 +01:00
Sam Brannen dafc7a2aab Apply "instanceof pattern matching" in remainder of spring-oxm module
See gh-30067
2023-03-06 17:07:49 +01:00
Sam Brannen b7288a4073 Apply "instanceof pattern matching" in remainder of spring-orm module
See gh-30067
2023-03-06 17:03:02 +01:00
Sam Brannen 004a144bdc Apply "instanceof pattern matching" in remainder of spring-messaging module
See gh-30067
2023-03-06 16:23:10 +01:00
Sam Brannen 09b60220d8 Apply "instanceof pattern matching" in remainder of spring-jms module
See gh-30067
2023-03-06 15:32:07 +01:00
Sam Brannen 7f1b81ff53 Apply "instanceof pattern matching" in remainder of spring-jdbc module
See gh-30067
2023-03-06 15:18:05 +01:00
Sam Brannen 8175a3bf09 Polishing 2023-03-06 15:17:39 +01:00
Sam Brannen 7eadedae36 Apply "instanceof pattern matching" in spring-jcl module
See gh-30067
2023-03-06 14:56:53 +01:00
Sam Brannen cb4f93561e Apply "instanceof pattern matching" in remainder of spring-expression module
See gh-30067
2023-03-06 14:45:28 +01:00
Sam Brannen f3cb331e4e Optimize find[Getter|Setter]ForProperty() in ReflectivePropertyAccessor 2023-03-06 14:45:28 +01:00
Sam Brannen 74f6725a37 Update copyright headers 2023-03-06 13:56:33 +01:00
Enric Sala edf0ae77e5 Avoid rollback after a commit failure in TransactionalOperator
A failure to commit a reactive transaction will complete the
transaction and clean up resources. Executing a rollback at
that point is invalid, which causes an
IllegalTransactionStateException that masks the cause of the
commit failure.

This change restructures TransactionalOperatorImpl and
ReactiveTransactionSupport to avoid executing a rollback after
a failed commit. While there, the Mono transaction handling in
TransactionalOperator is simplified by moving it to a default
method on the interface.

Closes gh-27572
2023-03-06 10:05:59 +01:00
Sam Brannen 95481018d0 Apply "instanceof pattern matching" in spring-core-test
See gh-30067
2023-03-05 19:37:59 +01:00
Sam Brannen 56523d5014 Polishing 2023-03-05 19:37:11 +01:00
Sam Brannen 8ebd746d69 Apply "instanceof pattern matching" in remainder of spring-core module
See gh-30067
2023-03-05 19:09:32 +01:00
Sam Brannen 162c09d036 Apply "instanceof pattern matching" in remainder of spring-context-support module
See gh-30067
2023-03-05 19:09:32 +01:00
Sam Brannen 7766b518d3 Apply "instanceof pattern matching" in spring-context-indexer module
See gh-30067
2023-03-05 19:09:32 +01:00
Sam Brannen 24de8c6f4c Apply "instanceof pattern matching" in remainder of spring-context module
See gh-30067
2023-03-05 19:09:32 +01:00
Sam Brannen d9500e60a1 Apply "instanceof pattern matching" in remainder of spring-beans module
See gh-30067
2023-03-05 19:09:32 +01:00
Sam Brannen e5d20a4f9d Convert EventExpressionRootObject to a record 2023-03-05 19:09:32 +01:00
Sam Brannen 37458d28d9 Covert InterceptorAndDynamicMethodMatcher to a record 2023-03-05 19:09:32 +01:00
Juergen Hoeller c9aba1eaad Enable Jetty 12 support in WebFlux
Closes gh-29575
2023-03-04 17:37:26 +01:00
Sam Brannen 3854861a8a Polishing 2023-03-03 15:39:24 +01:00
Spring Builds 3dd0fbfb57 Next development version (v6.0.7-SNAPSHOT) 2023-03-02 17:45:24 +00:00
Sam Brannen 57b838ddda Upgrade to spring-asciidoctor-backends 0.0.5 2023-03-02 16:50:48 +01:00
Sam Brannen 8c784085d2 Update copyright dates 2023-03-02 16:22:53 +01:00
Sam Brannen c0a1e1718e Polishing 2023-03-02 16:22:42 +01:00
Sam Brannen fe29e734ae Revise documentation for @AspectJ argument name resolution algorithm
Closes gh-30026
2023-03-02 15:26:21 +01:00
rstoyanchev 50c3a62589 Upgrade to Reactor Netty 2022.0.4
Closes gh-30063
2023-03-02 13:58:57 +00:00
Juergen Hoeller a936a6a8ce Javadoc-only reference to SubscribeMapping from simp package (-> package dependency cycle)
See gh-30002
2023-03-02 13:57:17 +01:00
Juergen Hoeller 8d112b8514 Test for explicit URI decoding in convertClassLoaderURL
See gh-30031
2023-03-02 13:33:53 +01:00
Juergen Hoeller f8cb0fa2a0 Custom resolution of preferred constructors for createBean(Class)
Avoids side effects of traditional AUTOWIRE_CONSTRUCTOR algorithm.

Closes gh-30041
2023-03-02 12:54:26 +01:00
rstoyanchev c56c16d7ba Polishing contribution
Closes gh-30010
2023-03-02 11:28:16 +00:00
Patrick Strawderman df1f8139cc Use Content-Length for optimal read to byte[]
If content-length is available, pass it to readNBytes in
ByteArrayHttpMessageConverter. When the content length is less than
the internal buffer size in InputStream (8192), this avoids a copy,
as readNBytes will return the buffer directly. When the content length
is greater than the buffer size used in InputStream, passing the
content-length at least avoids over-allocating the final buffer (e.g.,
if the content length were 8193 bytes, 1 byte more than the default
buffer size).

If the content length isn't present or is too large to represent as
an integer, fall back to the default behavior of readAllBytes by
passing in Integer.MAX_VALUE.

See gh-30010
2023-03-02 11:19:57 +00:00
rstoyanchev 9624ea392a Resolve baseUrl to String vs temporary URI
Along the lines of what was suggested in #30047.

Closes gh-30062
2023-03-02 10:20:05 +00:00
Violeta Georgieva 682a4d5353 Prefer request hostName and hostPort in ReactorServerHttpRequest
See gh-30062
2023-03-02 10:20:05 +00:00
Juergen Hoeller 4419d56178 Explicit URI decoding for FileSystemResource in convertClassLoaderURL
Also raising the log level to warn for file system retrieval failures.

Closes gh-30031
2023-03-02 10:18:13 +01:00
Sam Brannen 244c97993b Update documentation for @AspectJ argument name resolution algorithm
Closes gh-30026
2023-03-01 17:06:38 +01:00
Sam Brannen 9d28fe90f5 Simplify AbstractAspectJAdvisorFactory internals 2023-03-01 16:04:43 +01:00
Sébastien Deleuze d2a4ac519c Update build badge and link 2023-03-01 15:51:07 +01:00
Sébastien Deleuze dbbebf541d Refine reflection hints handling for anonymous class
Before this commit, anonymous classes could throw an
unexpected NullPointerException in
ReflectionsHint#registerType and lambdas entries could
be created in the related generated reflect-config.json.

This commit refines how anonymous classes are handled by
explicitly checking for null class and canonical name in
ReflectionTypeReference#of, while skipping such class in
ReflectionHints#registerType in order to keep a lenient
behavior.

Closes gh-29774
2023-03-01 15:41:30 +01:00
rstoyanchev fe73c630da Use URI String if necessary in ReactorClientHttpConnector
Closes gh-30053
2023-03-01 11:47:49 +00:00
Sam Brannen 3456fd054f Remove obsolete MetadataAwareAspectInstanceFactory Javadoc
We no longer have any JDK 5 related limitations imposed on us as was
the case when MetadataAwareAspectInstanceFactory was introduced; however,
MetadataAwareAspectInstanceFactory will remain as a specialized
sub-interface of AspectInstanceFactory.
2023-02-28 16:42:57 +01:00
Sam Brannen 5fd75dd27b Add missing package-info.java file for autoproxy.target package 2023-02-28 16:38:03 +01:00
Sam Brannen 6c29a5779e Fix .gitignore pattern for Maven "target" folders
Rationale: changes in org.springframework.aop.framework.autoproxy.target
were previously ignored since the "target" package was ignored by the
previous "eager" pattern for "target" folders.
2023-02-28 16:36:32 +01:00
Sam Brannen e9413b93c6 Apply "instanceof pattern matching" in BeanFactoryAdvisorRetrievalHelper 2023-02-28 16:33:56 +01:00
Radek Kraus 4cc02fe3bc Protect JMS connection creation against prepareConnection errors
This commit uses a local variable for the creation of a new JMS
Connection so that a rare failure in prepareConnection(...) does not
leave the connection field in a partially initialized state.

If such a JMSException occurs, the intermediary connection is closed.
This commit further defends against close() failures at that point,
by logging the close exception at DEBUG level. As a result, the original
JMSException is always re-thrown.

Closes gh-29116
See gh-29115
2023-02-28 16:10:05 +01:00
Sam Brannen b1cf832c28 Apply "instanceof pattern matching" in spring-tx
This commit applies "instanceof pattern matching" to the rest of the
code in spring-tx.

See gh-30019
2023-02-28 16:03:37 +01:00
Sam Brannen 34e5ce9360 Fix Checkstyle violations
See gh-30019
2023-02-28 16:03:37 +01:00
diguage 375114defa Apply "instanceof pattern matching" in spring-tx
Closes gh-30019
2023-02-28 15:33:59 +01:00
Sam Brannen 00c2c1d2a1 Polish "Support @Value for record injection"
See gh-28774
2023-02-28 13:12:15 +01:00
nanfeng 7c9fc575ff Support @Value for record injection
Add @Value support for record injection by disabling populateBean() for
records, since records are immutable.

See gh-28770
Closes gh-28774
2023-02-28 13:01:13 +01:00
rstoyanchev c859211f7a Polishing contribution
Closes gh-29691
2023-02-27 17:37:28 +00:00
Baljit Singh 4dbe9d6709 Use ContextView in ServerWebExchangeContextFilter 2023-02-27 17:20:16 +00:00
rstoyanchev acedbfbaba Polishing contribution
Closes gh-30014
2023-02-27 17:18:53 +00:00
Sergiu Prodan 5d6f151031 Update Javadoc of ServletRequestPathFilter
See gh-30014
2023-02-27 17:10:27 +00:00
Sam Brannen a09495d4e9 Polish contribution
See gh-30034
2023-02-27 16:38:55 +01:00
1993heqiang 5cab6a1f3a Fix "Configuring a Global Date and Time Format" example
Closes gh-30034
2023-02-27 16:33:38 +01:00
Sébastien Deleuze a02a017e6e Add tests for SpEL on Kotlin suspending function parameters
This was broken at some point but seems to work now.
This commit adds related tests.

Closes gh-26867
2023-02-27 16:09:43 +01:00
Sébastien Deleuze 9f2f93129b Move HttpServiceProxyFactoryExtensions.kt to spring-web module
HttpServiceProxyFactoryExtensions.kt has been mistakenly created
in spring-webflux module instead of spring-web, breaking JPMS for
WebFlux users.

This commit moves this file and related tests to the spring-web
module.

Closes gh-30042
2023-02-27 14:48:28 +01:00
Johnny Lim 475ac6ef5d Fix KotlinCoroutinesUtilsTests.invokeNonSuspendingFunctionWithContext()
Closes gh-30029
2023-02-27 14:02:49 +01:00
Sébastien Deleuze 79f43041ad Catch defensively validator exceptions in AOT processing
An ArrayIndexOutOfBoundsException is thrown by
Validator.getConstraintsForClass when processing Kotlin beans
with extensions functions (Kotlin or Hibernate Validator bug).

This commit catches those exceptions and report them as warning
without the full stactrace, and report as well other
ones thrown as errors with the full stracktrace.

Closes gh-30037
2023-02-27 12:06:37 +01:00
Sam Brannen edb4a3467a Update copyright headers 2023-02-26 18:37:18 +01:00
Sam Brannen 024d02225c Apply "instanceof pattern matching" to remaining Validation classes 2023-02-26 18:37:18 +01:00
Sam Brannen 9305a64a50 Polish contribution
See gh-29994
2023-02-26 18:30:14 +01:00
divcon 40672c3715 Apply "instance of patten matching" in context and messaging modules
Closes gh-29994
2023-02-26 18:30:14 +01:00
Sam Brannen 7c50464bba Polishing 2023-02-26 18:30:14 +01:00
Patrick Strawderman 0d10d4beee Initialize ArrayList with exact size in HttpHeaders 2023-02-24 17:09:58 +00:00
Sébastien Deleuze f60bec986f Add missing @Nullable annotations to LogMessage methods
Closes gh-30006
2023-02-24 17:45:50 +01:00
Sébastien Deleuze 6825a842b5 Support package private methods on CGLIB proxies with AOT
Closes gh-29582
Closes gh-29764
2023-02-24 17:00:41 +01:00
Sam Brannen 7ace9aa429 Polish AspectJAdviceParameterNameDiscoverer
This commit also converts PointcutBody to a record.
2023-02-24 11:40:34 +01:00
Sam Brannen 8979ac789f Fix layout in "Determining Argument Names" section 2023-02-24 11:40:34 +01:00
Sébastien Deleuze 9cb4c5565a Fix proxy hint Kotlin extensions
Closes gh-30025
2023-02-24 10:46:27 +01:00
Sam Brannen 08f38c52c7 Revise AspectJ examples in the reference manual
The extensive use of shared pointcuts can be confusing to users who
read a given AspectJ example without having read the "Sharing Common
Pointcut Definitions" section of the AOP chapter.

The primary purpose of this commit is therefore to reduce the extensive
use of shared pointcuts in AspectJ examples by favoring "inline
pointcuts" over shared "named pointcuts" and by cross referencing the
declaration of shared pointcuts when they are used.

Since the affected parts of the documentation are intertwined, this
commit also refactors the examples to use a common "com.xyz" package
namespace for greater clarity.

In order to avoid the need to escape asterisks (\*) in examples, this
commit also removes the `subs="quotes"` configuration for code blocks.

Closes gh-30003
2023-02-24 10:21:32 +01:00
Sam Brannen afa936e985 Polish AOP chapter 2023-02-24 10:21:32 +01:00
AlexElin 3677d3597b Improve @Lazy's javadoc by adding @code tag 2023-02-23 17:27:01 +00:00
Sam Brannen 76bc7deb8e Polish AOP chapter 2023-02-22 17:30:20 +01:00
Sam Brannen 6c3cb5d2e0 Revise chomp and fold settings in reference documentation
This commit overrides the `chomp` and `fold` settings for
spring-asciidoctor-backends on a per-block basis.

Closes gh-30001
2023-02-22 12:49:20 +01:00
Sam Brannen 3ce71932c0 Polish reference docs 2023-02-22 12:48:40 +01:00
Sam Brannen 3a9c7524f0 Polishing 2023-02-22 11:21:00 +01:00
Sam Brannen ce66b251ab Fix Javadoc for MockRestRequestMatchers 2023-02-21 22:13:41 +01:00
Sébastien Deleuze 626a7fc52a Add native support for @SubscribeMapping and @MessageExceptionHandler
Closes gh-30002
2023-02-21 18:47:30 +01:00
Sébastien Deleuze 3d8455b257 Add unit tests for CoroutinesUtils
Closes gh-29968
2023-02-21 10:20:00 +01:00
Sébastien Deleuze fd38c23699 Refine CoroutinesUtils#invokeSuspendingFunction contract
This commit refines CoroutinesUtils#invokeSuspendingFunction in order
to clarify the behavior when used on a non suspending function, and
support usages with or without the Continuation argument.

Closes gh-30005
2023-02-21 10:20:00 +01:00
Sam Brannen e47418c948 Polish attributes.adoc 2023-02-20 17:23:07 +01:00
Sam Brannen d6de374424 Restore package & import statements in AOP examples in ref docs
See gh-30001
2023-02-20 17:23:07 +01:00
Sam Brannen b437b7be34 Introduce test for perthis with @Aspect and shared pointcut
This commit modifies PerThisAspect (which is used indirectly in
AspectJAutoProxyCreatorTests.perThisAspect()) so that it uses a shared
@Pointcut for both the @Aspect and @Around declarations, in order to
refute claims made in gh-29998 that the documentation in the reference
manual is incorrect.
2023-02-20 17:23:07 +01:00
Sam Brannen 2d56505ea9 Polishing 2023-02-20 16:49:27 +01:00
Sam Brannen 8a44b6445d Revise queryParam() and header() support in MockRestRequestMatchers
See gh-29953
See gh-29964
2023-02-19 18:52:44 +01:00
Sam Brannen 6d24e62e83 Polishing 2023-02-19 17:43:31 +01:00
Johnny Lim 2d62be8590 Fix Javadoc since for MockRestRequestMatchers queryParam() and header() (#29986)
See gh-29953
See gh-29964
2023-02-19 16:04:53 +01:00
Sam Brannen 2e1374b459 Update copyright headers 2023-02-19 13:41:36 +01:00
Sam Brannen bb6150e44e Use correct Kotlin file extension in update_copyright_headers.sh 2023-02-19 13:41:36 +01:00
Sam Brannen 38a4f23f16 Polishing 2023-02-19 13:41:36 +01:00
Johnny Lim 6739ca82ce Polishing
See gh-23846
See gh-29955
Closes gh-29992
2023-02-19 13:41:36 +01:00
Arjen Poutsma 88e6544d9d Fix regression in WebFlux support for WebDAV methods
This commit ensures that WebFlux's RequestMethodsRequestCondition
supports HTTP methods that are not in the RequestMethod enum.

- RequestMethod::resolve is introduced, to convert from a HttpMethod
(name) to enum values.
- RequestMethod::asHttpMethod is introduced, to convert from enum value
to HttpMethod.
- HttpMethod::valueOf replaced Map-based lookup to a switch statement
- Enabled tests that check for WebDAV methods

See gh-27697
Closes gh-29981
2023-02-17 12:46:26 +01:00
Spring Builds 1999c78350 Next development version (v6.0.6-SNAPSHOT) 2023-02-15 16:20:19 +00:00
Arjen Poutsma afd67a0deb Fix DataBufferUtils::write AsynchronousFileChannel race condition
See gh-29943
2023-02-15 15:38:48 +01:00
Jerome Prinet dcaddddbc5 Update Gradle Enterprise plugin
Closes gh-29969
2023-02-15 14:43:31 +01:00
Johnny Lim ce3be72e7f Polish 2023-02-15 22:22:58 +09:00
Juergen Hoeller 3e3f046dc9 Polishing 2023-02-15 12:57:44 +01:00
rstoyanchev 5847454aa4 Prefer local hostAddress in ReactorServerHttpRequest
Closes gh-28601
2023-02-15 10:26:49 +00:00
Sébastien Deleuze 569df6eecc Refine ModelAttributeMethodProcessor Kotlin exception handling
This commit refines ModelAttributeMethodProcessor Kotlin exception
handling in order to throw a proper MethodArgumentNotValidException
instead of a NullPointerException when Kotlin null-safety constraints
are not fulfilled, translating to an HTTP error with 400 status code
(Bad Request) instead of 500 (Internal Server Error).

Closes gh-23846
2023-02-15 11:02:46 +01:00
Juergen Hoeller 979118c1eb Polishing 2023-02-15 10:13:16 +01:00
Juergen Hoeller f87a87e29d Consistent ordering of Resource methods
See gh-24651
2023-02-15 10:13:03 +01:00
Juergen Hoeller 0619e19140 Upgrade to Reactor 2022.0.3
Closes gh-29900
2023-02-15 10:12:48 +01:00
Sébastien Deleuze ac521a366a Refine instance supplier check in BeanDefinitionMethodGenerator
This commit refines the instance supplier check in
BeanDefinitionMethodGenerator constructor in order to allow
overriding by an AOT contribution.

Closes gh-29556
2023-02-15 09:32:00 +01:00
rstoyanchev 9a4df5a97d DefaultHandlerExceptionResolver respects custom ModelAndView
Closes gh-29971
2023-02-14 20:13:15 +00:00
rstoyanchev f54e1ef880 Initialize propertyName
MethodArgumentTypeMismatchException and
MethodArgumentConversionNotSupportedException are TypeMismatchException
subclasses with MethodParameter information and should initialize
propertyName in TypeMismatchInformation.

Closes gh-29959
2023-02-14 18:12:48 +00:00
rstoyanchev a3efcabe0c Polishing contribution
Closes gh-29954
2023-02-14 17:40:12 +00:00
James Yuzawa dd3dc2d0d0 Pass URI to Reactor Netty HttpClient
See gh-29954
2023-02-14 17:40:12 +00:00
Brian Clozel 2578e3894e Upgrade to Micrometer 1.10.4
Closes gh-29899
2023-02-14 17:10:10 +01:00
Brian Clozel 5335778d8d Polish
See gh-29955
2023-02-14 17:08:24 +01:00
Toshiaki Maki 616e7779e0 Add RFC-8246 support to CacheControl
This commit adds the "immutable" Cache-Control directives in `CacheControl`.

Closes gh-29955
2023-02-14 17:08:24 +01:00
Juergen Hoeller 84c7f07364 Consistent @Bean method return type for equivalence with XML example
Closes gh-29338
2023-02-14 16:36:31 +01:00
Juergen Hoeller ac429a4ef7 Restore fallback to request attributes in FreeMarker template model
Closes gh-29787
2023-02-14 16:36:07 +01:00
Arjen Poutsma 12d4dc1bae Polishing external contribution
This commit makes several changes to PR #24651.

- Add byte[] getContentAsByteArray() on Resource.
- Remove getContentAsString() from Resource, as it relied on the default
charset which is not reliable.
- Add getContentAsString() to EncodedResource, as a charset is provided
through the constructor.

See gh-24651
2023-02-14 14:56:34 +01:00
Derrick Anderson 4da2499613 Introduce Resource::getContentAsString
This commit introduces the getContentAsString method to Resource,
returning the string contents of the resource.

Closes gh-24651
2023-02-14 14:37:40 +01:00
rstoyanchev 18896aceca Adapt empty path to "/" in MockMvc
Closes gh-29933
2023-02-14 12:01:59 +00:00
Juergen Hoeller d7824c7831 Polishing 2023-02-14 11:00:01 +01:00
Juergen Hoeller f91b2249b3 Upgrade to Netty 4.1.89 2023-02-14 10:58:15 +01:00
Juergen Hoeller 33b329e13a Latest patch from ASM master
See gh-29910
2023-02-14 10:58:03 +01:00
rstoyanchev b23316302d Avoid blocking in InMemoryWebSessionStore#changeSessionId
Closes gh-29212
2023-02-13 18:39:19 +00:00
rstoyanchev dc843adb45 Improve docs on {@code Accept-Language} negotiation
Closes gh-28673
2023-02-13 18:39:19 +00:00
rstoyanchev 9fcafd2650 WebSocketConnectionManager accepts prepared URI
Closes gh-28524
2023-02-13 18:39:19 +00:00
rstoyanchev ccbb4bdd27 PayloadMethodArgumentResolver supports Optional
Closes gh-28945
2023-02-13 18:39:19 +00:00
rstoyanchev 5b79a57f14 Polishing
See gh-28945
2023-02-13 18:39:19 +00:00
Simon Baslé 189d4e3e4c Allow MockRest to match header/queryParam value list with one Matcher
This commit adds a `header` variant and a `queryParam` variant to the
`MockRestRequestMatchers` API which take a single `Matcher` over the
list of values.

Contrary to the vararg variants, the whole list is evaluated and the
caller can choose the desired semantics using readily-available iterable
matchers like `everyItem`, `hasItems`, `hasSize`, `contains` or
`containsInAnyOrder`...

The fact that the previous variants don't strictly check the size of the
actual list == the number of provided matchers or expected values is
now documented in their respective javadocs.

See gh-28660
Closes gh-29953
2023-02-13 17:51:01 +01:00
Arjen Poutsma 79a1fcb099 Polishing DataBuffer::write(CharSequence, Charset)
See gh-29943
2023-02-13 16:34:04 +01:00
Sébastien Deleuze 026be04b75 Polish CoroutinesAnnotationTransactionInterceptorTests 2023-02-13 15:34:01 +01:00
Sébastien Deleuze 45ae00fda3 Propagate the context in Coroutines transactions
This commit ensures that CoroutineContext is properly
propagated in transactional suspending functions. Both
annotation and functional variants are supported.

Closes gh-27308
2023-02-13 15:34:01 +01:00
Arjen Poutsma 3e2f58cdd2 Offer restricted access to DataBuffer's ByteBuffer
This commit introduces DataBuffer::readableByteBuffers and
DataBuffer::writableByteBuffers, allowing restricted access to the
ByteBuffer used internally by DataBuffer implementations.

Closes gh-29943
2023-02-13 15:28:29 +01:00
Boris Fox 72926c29f9 Temporarily set LocaleContextHolder to provide validation message interpolator with locale context (fixes SPR-17231). 2023-02-13 11:45:45 +00:00
rstoyanchev f5c1e2ffa1 Polishing contribution
Includes small refactoring in DefaultServerWebExchange and adjustment
of initMultipartData to get involved for any "multipart/" prefixed
media type.

In addition, "multipart/related" is now in the list of media types
supported by FormHttpMessageConverter, which aligns it with
MultipartHttpMessageReader.

Closes gh-29671
2023-02-13 11:40:36 +00:00
Ronny Perinke 67df0756cd Support multipart/mixed and related in DefaultServerWebExchange
See gh-29671
2023-02-13 10:39:23 +00:00
rstoyanchev 17bc3facaa Polishing contribution
Closes gh-29220
2023-02-13 09:16:33 +00:00
Anatolii Zhmaiev 1de9426df0 Set reason for WebSocket CloseStatus.SESSION_NOT_RELIABLE
See gh-29220
2023-02-13 09:16:33 +00:00
rstoyanchev 27fdf56ed5 Polishing contribution
Closes gh-29949
2023-02-13 07:52:01 +00:00
Yanming Zhou 55392dfbd7 Define xml namespace as constant
See gh-29949
2023-02-13 07:51:30 +00:00
Juergen Hoeller ca9439657e Detect MSSQL code 2601 as duplicate key (aligned with sql-error-codes.xml)
Closes gh-29950
2023-02-09 14:59:09 +01:00
Sébastien Deleuze 5819d76d98 Document AOT limitation regarding to @Bean type
Closes gh-29944
2023-02-09 10:34:42 +01:00
Johnny Lim 431ae03447 Polish
Closes gh-29928
2023-02-09 09:54:19 +01:00
rstoyanchev a7ec6b4a2a Unwrap session before selecting stats counter
See gh-29375
2023-02-08 18:12:42 +00:00
rstoyanchev e5ff54955f ProblemDetail XML support via Jackson
Closes gh-29927
2023-02-08 10:39:41 +00:00
rstoyanchev 9c0b28ffdc Fix StringIndexOutOfBoundsException
Closes gh-29588
2023-02-07 20:17:07 +00:00
Sam Brannen 08e7f5a292 Clearly document that DataClassRowMapper supports Java records
Closes gh-29814
2023-02-07 19:05:47 +01:00
Sam Brannen 5f458e2578 Polish RowMapper tests 2023-02-07 19:05:38 +01:00
Sam Brannen 7584dfc79a Avoid confusing terminology in BeanPropertyRowMapper
Prior to this commit, the term "field" was sometimes used to refer to a
database column and sometimes used to refer to a bean property, which
lead to confusion in the Javadoc as well as within the code.

This commit addresses this by avoiding use of the term "field".
2023-02-07 18:31:50 +01:00
Juergen Hoeller dc2c33e0ab Upgrade to Hibernate ORM 5.6.15, FreeMarker 2.3.32, TestNG 7.7.1, XMLUnit 2.9.1 2023-02-07 17:20:11 +01:00
Juergen Hoeller dbf360997a Convert non-pattern ClassLoader results to FileSystemResource if possible
Closes gh-29934
2023-02-07 17:19:23 +01:00
Juergen Hoeller b719a87061 Suggest AOP proxy or CGLIB_CLASS_SEPARATOR check instead of isCglibProxyClass
See gh-22706
2023-02-07 17:19:12 +01:00
Sam Brannen 97e557c886 Update copyright headers 2023-02-07 16:37:18 +01:00
Manthan Bhatt 89c170b095 Restrict forwards in MockMvcWebConnection to 100
This change restricts the maximum number of forwards in MockMvcWebConnection to 100,
in case a forward is configured in a way that causes a loop. This is necessary in HtmlUnit
backed tests, unlike in classic MockMvc tests in which the forwards are not actually resolved.

Closes gh-29483
Closes gh-29557

Co-authored-by: Simon Baslé <sbasle@vmware.com>
2023-02-07 16:23:56 +01:00
Sam Brannen 482482f62b Revise Testcontainers examples based on feedback
Closes gh-29939
2023-02-07 15:55:00 +01:00
Sébastien Deleuze a2a676241c Introduce HttpServiceProxyFactory#createClient Kotlin extension
Closes gh-29942
2023-02-07 15:50:30 +01:00
Johnny Lim 28602ad012 Add MockMvc.multipart() Kotlin extensions with HttpMethod
See gh-28545
See gh-28631
Closes gh-28634
2023-02-07 15:13:17 +01:00
Sam Brannen 8e9dc3590d Update @DynamicPropertySource examples regarding changes in Testcontainers
Closes gh-29939
2023-02-07 13:34:22 +01:00
Sébastien Deleuze b8a7aa9395 Refine ControllerMappingReflectiveProcessor documentation
Closes gh-29938
2023-02-07 12:00:07 +01:00
Sébastien Deleuze cb63164593 Refine Coroutines support in HttpServiceProxyFactory
Closes gh-29527
2023-02-07 11:07:30 +01:00
Sébastien Deleuze 9f061f1eec Refine KotlinWebClientHttpServiceProxyTests
See gh-29527
2023-02-07 11:07:30 +01:00
Donghyeon Kim 1d4bf58e8d Support Coroutines in HttpServiceProxyFactory
See gh-29527
2023-02-07 11:07:29 +01:00
rstoyanchev ce85fdc5c7 Always use application/problem+json with ProblemDetail
See gh-gh-29588
2023-02-07 08:57:24 +00:00
rstoyanchev 7851994a17 Refine empty path handling in MvcUriComponentsBuilder
Return "" from methods that obtain controller or method mapping to
avoid side effect of a trailing slash appearing.

Closes gh-29897
2023-02-07 08:57:24 +00:00
Sam Brannen 84bc1dfa89 Polish RowMapper tests 2023-02-03 17:47:50 +01:00
Sam Brannen 159a3e71f2 Clarify semantics of primitivesDefaultedForNullValue in BeanPropertyRowMapper
Closes gh-29923
2023-02-03 17:47:50 +01:00
Sam Brannen adbba712d6 Polish Javadoc for RowMappers 2023-02-03 17:47:50 +01:00
danu e050c37158 Release R2DBC connection when cleanup fails in transaction
When using R2dbcTransactionManager, connection will not be released if
it encounters error while doing `afterCleanup` steps. As `afterCleanup`
can use a database connection when doing `setAutoCommit(true)`, it can
fail under some conditions where the connection is not reliable.

This leads to the Connection not being released.

This commit ensures that inner steps of the `doCleanupAfterCompletion`
are protected against errors, logging the errors and continuing the
cleanup until the last step, which releases the connection.

Closes gh-29703

Co-authored-by: Simon Baslé <sbasle@vmware.com>
2023-02-03 17:33:55 +01:00
Brian Clozel eff1a1a664 Upgrade to Artifactory Resource 0.0.18 2023-02-03 00:38:57 +01:00
Brian Clozel 8838e0d825 Use Docker Hub credentials for CI release tasks 2023-02-03 00:34:58 +01:00
Brian Clozel 0d21f0b1d4 Remove Java 18 from CI image 2023-02-02 21:02:01 +01:00
Brian Clozel 41aeb07585 Rely only on Docker Hub for fetching OCI images 2023-02-02 20:56:50 +01:00
Sam Brannen 70aaec2a8d Use static imports for AssertJ assertions in Kotlin tests 2023-02-02 20:12:42 +01:00
Sam Brannen 3029b7b9be Fix build 2023-02-02 20:12:18 +01:00
Brian Clozel 708f600afe Document how to mark an observation as an error
The `ServerHttpObservationFilter` implementations record observations
for processed HTTP exchanges. The `Observation.Context` contains various
metadata contributed by the observation convention. The instrumentation
can also mark the observation as an error by setting any `Throwable` on
the context.

Because the instrumentation is done as filters, only exceptions reaching
the filter can be considered. Any error handled at a lower level by the
Framework can, or cannot be considered as an error for an observation.

This commit documents how a web application should opt-in for
considering a handled exception as an error for the current observation.

Closes gh-29848
2023-02-02 18:19:17 +01:00
Sébastien Deleuze f7418704de Add ResponseEntity.ofNullable()
To deal with non-Optional nullable objects.

Closes gh-29117
2023-02-02 16:42:42 +01:00
Brian Clozel 7440afe571 Document how to enable HTTP client Observation instrumentation
This commit documents how Observation instrumentation should be
activated for `RestTemplate` and `WebClient`: they both need an
`ObservationRegistry` configured to create and record actual
observations.
This is being done automatically in Spring Boot if auto-configured
builders (`RestTemplateBuilder`, `WebClient.Builder`) are used.

Closes gh-29904
2023-02-02 16:03:11 +01:00
Sam Brannen bc583ea74f Polish "Use switch expressions in SpEL's CodeFlow"
See gh-29020
2023-02-02 15:14:34 +01:00
Roman Ivanov dcda127b60 Use switch expressions in SpEL's CodeFlow
Closes gh-29020
2023-02-02 15:02:10 +01:00
Sam Brannen ac6385025b Polishing 2023-02-02 15:02:10 +01:00
Sébastien Deleuze e170c16b02 Refine TransactionalOperator.executeAndAwait nullability
Before this commit, TransactionalOperator.executeAndAwait had a rigid
null-safety handling. This commit takes advantage of Kotlin capability
to allow to deal with both non-null and nullable depending on the
nullability of the lambda.

Closes gh-29919
2023-02-02 14:39:33 +01:00
Olga Maciaszek-Sharma 4beb05ddb3 Add native support for RSocketExchange.
This commit introduces new AOT processors that look for
`@RSocketExchange` annotated methods on interfaces implemented by beans
and registers reachability metadata accordingly:

* JDK proxies for the beans themselves
* invocation reflection for annotated methods
* binding reflection for arguments and return types

This allows to compile such clients to Native Images.

Closes gh-29877
2023-02-01 18:22:33 +01:00
Juergen Hoeller 06fd6ecf1f Upgrade to Groovy 4.0.8, Tomcat 10.1.5, Netty 4.1.87, Undertow 2.3.3, Jackson 2.14.2, Woodstox 6.5, Mockito 5.1.1, AssertJ 3.24.2, HtmlUnit 2.70, Checkstyle 10.7 2023-02-01 18:18:52 +01:00
Juergen Hoeller b8827d8e11 Clarify postProcessBeanFactory lifecycle state
Closes gh-29064
2023-02-01 18:18:37 +01:00
Sam Brannen 23a60aeb27 Sync MockHttpServletRequest implementations 2023-02-01 16:28:38 +01:00
Arjen Poutsma 58fbf60d2d Include quoted printable filename in ContentDisposition::toString
This commit ensures the ContentDisposition class prints the filename in
both in the regular filename parameter and the extended filename*
parameter (RFC 5987).
Quoted printable (RFC 2047) is used to encode any non-ASCII characters
in the regular filename parameter.

Closes gh-29861
2023-02-01 13:40:03 +01:00
Sébastien Deleuze ebca9f726f Provide Kotlin extensions for RuntimeHints
To be able to register hints using for example registerType<MyClass>()
instead of registerType(MyClass::class.java).

Closes gh-29831
2023-02-01 12:04:18 +01:00
Brian Clozel 5fd73f0bae Fix MockHttpServletRequest InputStream for empty body
Prior to this commit, a recent change applied in gh-29125 changed the
behavior of `MockHttpServletRequest` instances. In case of an empty
request body, the returned `InputStream` would be static and could not
be reused across requests.
This could result in `java.io.IOException: Stream closed` exceptions if
a previous request was read.

This commit ensures that a new instance of an empty stream is returned
for each request instance.

Fixes gh-29901
2023-01-31 19:28:15 +01:00
Juergen Hoeller 16937c7ce5 Polishing 2023-01-31 18:15:28 +01:00
Juergen Hoeller c0c9ba5c2c Polishing 2023-01-31 16:14:32 +01:00
Juergen Hoeller 4d6249811e Explicit target ClassLoader for interface-based infrastructure proxies
Includes direct JDK Proxy usage instead of ProxyFactory where possible.

Closes gh-29913
2023-01-31 16:11:34 +01:00
Juergen Hoeller 3a08d643ed Upgrade to ASM master (including early support for Java 21 bytecode)
Closes gh-29910
2023-01-31 16:06:33 +01:00
Juergen Hoeller a74b86e812 Lazily load ContextLoader.properties (and lazily fail if not present)
Closes gh-29905
2023-01-31 16:05:07 +01:00
Juergen Hoeller 7c9dca3d2e Declare no-op close() method in order to avoid container-triggered shutdown call
Closes gh-29892
2023-01-31 16:03:26 +01:00
Brian Clozel 64219783f6 Start building with Reactor 2022.0.3-SNAPSHOT
See gh-29900
2023-01-31 15:37:04 +01:00
Brian Clozel 846a54aef4 Start building with Micrometer 1.10.4-SNAPSHOT
See gh-29899
2023-01-31 15:36:24 +01:00
Chen Jianbin 9e735d4f0f Normalize and optimize internals of CGLIB ParallelSorter
Closes gh-29516
2023-01-31 12:20:45 +01:00
Arjen Poutsma 0ddb394fd0 Register callback for HttpComponentsClientHttpConnector
This commit ensures that we register a result callback when executing
the request (next to the existing response callback), which gets
notified of invalid hosts and other connection issues.

Closes gh-29156
2023-01-31 12:16:43 +01:00
Sam Brannen f5a39308a0 Polishing 2023-01-31 11:54:21 +01:00
Johnny Lim e4ceb80678 Use EnumMap for DateFormatter.ISO_PATTERNS again
This commit changes to use `EnumMap` for `DateFormatter.ISO_PATTERNS`
again by partially reverting commit ba136dcf40.

See gh-29321
Closes gh-29485
2023-01-31 11:52:25 +01:00
Sam Brannen 64b89429cb Update copyright headers 2023-01-31 11:52:25 +01:00
Arjen Poutsma 8e843c242a Merge pull request #29853 from ThomHurks:cors-configuration-small-memory-optimization
* gh-29853:
  Polish external contribution
  Optimize CorsConfiguration::checkHeaders
2023-01-31 10:30:46 +01:00
Arjen Poutsma 635f010de3 Polish external contribution 2023-01-31 10:28:42 +01:00
Thom Hurks aac1ed6ee5 Optimize CorsConfiguration::checkHeaders
In the checkHeaders method, if allowedHeaders contains * the result
size can be requestHeaders.size(), but if this is not the case
then the result size can only be as big as the minimum value
of requestHeaders.size() and allowedHeaders.size().
requestHeaders.size() can be potentially big (user input) and
allowedHeaders.size() could be small, so this saves memory in
that case.

Closes gh-29853
2023-01-31 10:28:42 +01:00
Sam Brannen d5b0782700 Polishing 2023-01-31 10:10:44 +01:00
Sam Brannen ebe717ff6c Update release version due to backport
See gh-29790
See gh-29902
2023-01-31 10:00:18 +01:00
Sébastien Deleuze 89c7c6e9dd Fix bean validation on suspending function parameters
This commit leverages Hibernate Validator's own internal use
of standard Java reflection to perform validation on suspending
function, which fixes the ArrayIndexOutOfBoundsException previously
observed.

Validation of suspending function return values remains unsupported
as Hibernate Validator is not Coroutines aware.

Closes gh-23499
2023-01-31 09:41:47 +01:00
rstoyanchev 9c6fd3ed06 Consistently list supported media types
Add constructors to HttpMediaTypeNotSupportedException and
UnsupportedMediaTypeStatusException for a parse error that also accept
the list of supported media types to include in the response headers.

Closes gh-28062
2023-01-30 17:46:08 +00:00
rstoyanchev e564a0de46 Provide access to interceptors in AbstractHandlerMapping
Closes gh-28985
2023-01-30 17:46:08 +00:00
rstoyanchev 17f1eadb6f Polishing contribution
Closes gh-29610
2023-01-30 17:46:08 +00:00
divcon 09991f2492 Polishing in ResponseEntityResultHandler
See gh-files
2023-01-30 17:46:08 +00:00
rstoyanchev 15931532cd Polishing contribution
Closes gh-29868
2023-01-30 17:46:08 +00:00
Eden Tyler-Moss aa9f445422 Add @Nullable to ProblemDetail.setProperty argument
See gh-29868
2023-01-30 17:46:08 +00:00
rstoyanchev b578ff1296 Polishing contribution
Closes gh-29696
2023-01-30 17:46:08 +00:00
Kevin Yue cf9fc69d6b Don't swallow exception when logging response status
See gh-29696
2023-01-30 17:46:08 +00:00
Sam Brannen 04321d0577 Revise generated default name for @JmsListener subscription
The previous commit changed the generated default name for a JMS
subscription to <FQCN>#<method name> -- for example:

- org.example.MyListener#myListenerMethod

However, the JMS spec does not guarantee that '#' is a supported
character. This commit therefore changes '#' to '.' as the separator
between the class name and method name -- for example:

- org.example.MyListener.myListenerMethod

This commit also introduces tests and documentation for these changes.

See gh-29790
2023-01-30 17:42:00 +01:00
Sam Brannen 67c141f15b Polishing 2023-01-30 15:33:47 +01:00
fml2 ad4e0d9ad7 Improve generated default name for a @JmsListener subscription
Prior to this commit, when using durable subscribers with @JmsListener
methods that do not specify a custom subscription name the generated
default subscription name was always
org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.
Consequently, multiple such @JmsListener methods were assigned the
same subscription name which violates the uniqueness requirement.

To address this, MessagingMessageListenerAdapter now implements
SubscriptionNameProvider and generates the subscription name based on
the following rules.

- if the InvocableHandlerMethod is present, the subscription name will
  take the form of handlerMethod.getBeanType().getName() + "#" +
  handlerMethod.getMethod().getName().
- otherwise, getClass().getName() is used, which is analogous to the
  previous behavior.

Closes gh-29790
2023-01-30 15:19:45 +01:00
Arjen Poutsma c13dfc5144 Merge pull request #29889 from xavier-b:main
* gh-29889:
  Use DataBuffer::toString instead of CharBuffer
2023-01-30 15:03:04 +01:00
xavier-b b7d53ac9a4 Use DataBuffer::toString instead of CharBuffer
This commit ensures that converting DataBuffers to a String does not
use an expensive ByteBuffer allocation.

Closes gh-29889
2023-01-30 14:58:48 +01:00
Arjen Poutsma f9884e08af Polish external contribution 2023-01-30 14:19:41 +01:00
Vatsa 9819cb3eb5 Update HandlerFunctionAdapter::setAsyncRequestTimeout Javadoc
Closes gh-29882
2023-01-30 14:19:41 +01:00
Sam Brannen 9b0929c392 Update copyright headers 2023-01-30 13:00:22 +01:00
Arjen Poutsma af21ebe6c6 Fix CronExpression::parse javadoc
Closes gh-29884
2023-01-30 11:49:34 +01:00
Johnny Lim 0b446a94c5 Add Javadoc since for CLIENT_NAME enums
See gh-29839
Closes gh-29893
2023-01-30 10:10:31 +01:00
Brian Clozel 5dfa61eb0b Restrict "uri" KeyValue for client observations
Prior to this commit, the "uri" KeyValue for low cardinality metadata
would contain the entire uri template given to the HTTP client when
creating the request. This was a breaking change for existing metrics
dashboards, as previous support was removing the protocol, host and port
parts of the URI.
Indeed, this information is available in the "client.name" and
"http.uri" KayValue.

This commit parses and removes the protocol+host+port information from
the uri template for the "uri" KeyValue.

Fixes gh-29885
2023-01-30 10:09:45 +01:00
Brian Clozel b267547fb4 Contribute "uri" KeyValue for "/" client requests
Prior to this commit, client HTTP requests performed by `WebClient`
could miss the "uri" KeyValue for simple "/" requests.
This can happen when the baseUri is configured for the client with a
host and a root base path like "https://example.org/"; given the nature
of the `WebClient` API, in these cases, one can perform requests like
this:

```
WebClient client = WebClient.builder()
    .observationRegistry(registry)
    .baseUrl("https://example.org/")
    .build();

String response = client.get().retrieve().bodyToMono(String.class).block();
```

Such a call would contribute a `"none"` value for the `"uri"` KeyValue.

While only templates should be allowed for this keyvalue, we can assume
that requests to `"/"` should be recorded anyway and won't cause
cardinality explosion.

Fixes gh-29879
2023-01-30 10:09:41 +01:00
Sam Brannen e0f60dc09d Apply "instanceof pattern matching" in spring-context and polishing
See gh-29222
2023-01-29 14:21:15 +01:00
Adam Ostrožlík 8783075e40 Apply "instanceof pattern matching" and minor refactorings in spring-context
Closes gh-29222
2023-01-29 13:52:43 +01:00
Adam Ostrožlík f2cdced501 Apply minor polishing in spring-aop
Closes gh-28984
2023-01-29 13:32:33 +01:00
Sam Brannen 33a0e65135 Fix build due to Jackson Javadoc publication changes
The Jackson project no longer publishes Javadoc at
https://fasterxml.github.io which breaks the `javadoc` and `api` build
tasks due to their dependency on that web site for external Javadoc links.

As a workaround, we now reference Jackson's Javadoc via
https://www.javadoc.io.

See https://github.com/FasterXML/jackson-databind/issues/3440
Closes gh-29894
2023-01-29 12:35:13 +01:00
Sam Brannen e3fb0a39a2 Clean up compiler warnings 2023-01-28 21:18:19 +01:00
Sam Brannen 57fb072215 Include all Hibernate methods in SharedEntityManagerCreator's queryTerminatingMethods
Prior to this commit, we included Hibernate's Query.list() method in
SharedEntityManagerCreator's queryTerminatingMethods set but did not
include all of Hibernate's query-terminating methods.

To address this, this commit additionally includes the stream(),
uniqueResult(), and uniqueResultOptional() methods from Hibernate's
Query API in SharedEntityManagerCreator's query-terminating methods set.

Closes gh-29886
2023-01-28 20:42:21 +01:00
Sam Brannen 75046bbea0 Update copyright headers 2023-01-28 20:42:21 +01:00
Sam Brannen 8e2eeb5364 Apply "instanceof pattern matching" in spring-beans 2023-01-27 17:54:03 +01:00
Sam Brannen 556863d401 Polishing 2023-01-27 17:54:03 +01:00
rstoyanchev 6a6a35a0b9 Support method validation for interface-only proxies
Closes gh-29782
2023-01-27 14:46:22 +00:00
Brian Clozel 7da6e93597 Set WebClient Observation as current in reactor context
Prior to this commit, the `DefaultWebClient` would be instrumented for
client observations and would start/stop a `"http.client.requests"`
observation. This would not set this new observation as the current one
in the Reactor context under `ObservationThreadLocalAccessor.KEY`.
This means that potential child observations would not detect it as
their parent; this can happen if the Reactor Netty `HttpClient`
observation is enabled.

This commit ensures that the reactor context is properly populated for
upstream operators.

Fixes gh-29891
2023-01-27 14:42:08 +01:00
Brian Clozel 963274a915 Polish 2023-01-27 09:52:24 +01:00
Simon Baslé d72df5ace4 DatabaseClient uses SQL Supplier more lazily
This commit modifies the `DefaultDatabaseClient` implementation in order
to ensure lazier usage of the `Supplier<String>` passed to the sql
method (`DatabaseClient#sql(Supplier)`).

Since technically `DatabaseClient` is an interface that could have 3rd
party implementations, the lazyness expectation is only hinted at in the
`DatabaseClient#sql` javadoc.

Possible caveat: some log statements attempt to reflect the now lazily
resolved SQL string. Similarly, some exceptions can capture the SQL that
caused the issue if known. We expect that these always occur after the
execution of the statement has been attempted (see `ResultFunction`).
At this point the SQL string will be accessible and logs and exceptions
should reflect it as before. Keep an eye out for such strings turning
into `null` after this change, which would indicate the opposite.

Closes gh-29367
2023-01-26 14:35:31 +01:00
Brian Clozel 767f59a3ae Make client.name low cardinality keyvalue for client observations
Prior to this commit, the `"client.name"` key value for the
`"http.client.requests"` client HTTP observations would be considered as
high cardinality, as the URI host is technically unbounded.
In practice, the number of hosts used by a client in a given application
can be considered as low cardinality. This commit moves this keyvalue to
low cardinality so that it's present for both metrics and traces.

Closes gh-29839
2023-01-25 20:51:15 +01:00
Sam Brannen da088e58c3 Increase number of TOC levels to 4 in generated PDF documentation
Closes gh-29781
2023-01-24 15:31:14 +01:00
Arjen Poutsma 5502e61519 Support Optional in AbstractJackson2HttpMessageConverter
This commit introduces support for Optional in the
AbstractJackson2HttpMessageConverter, similar the existing support for
collection types were supported.

Closes gh-24498
2023-01-24 13:08:21 +01:00
Sébastien Deleuze ab9bea1d93 Polish RouterFunctionDsl KDoc 2023-01-23 13:28:10 +01:00
Sébastien Deleuze e6397c8a38 Infer reflection hints for Jackson annotation class attributes
Closes gh-29646
Closes gh-29386
2023-01-23 13:21:34 +01:00
Sébastien Deleuze 1e47f31210 Support code generation for Set with non-comparable elements
Closes gh-29792
2023-01-23 10:55:34 +01:00
Sébastien Deleuze 57b6f7e494 Infer hints for custom constraint validators
This commit introduces a BeanValidationBeanRegistrationAotProcessor
which adds reflection hints for custom ConstraintValidator discovered
on beans constructors, methods and properties.

Closes gh-29823
2023-01-23 10:15:58 +01:00
Brian Clozel d37ef61b30 Resolve static resources without wildcard pattern
Prior to this commit, the reactive `ResourceWebHandler` would only look
at the path within the current mapping when resolving static resources
to be served. This means that when registering a handler at
`"/resources/**"` with a `"classpath:/static/"` location, the handler
would process a `"GET /resources/file.txt"` as the `"/static/file.txt"`
classpath location.

When a developer registers a fixed pattern like `"/resources/file.txt"`
with the same location, the path within the handler mapping is empty as
there is no dynamic part in the given pattern. While the typical use
case for this feature is to register multiple resources at once with a
pattern, we should support a single registration like this.

This commit ensures that if the matching `PathPattern` for the current
request does not have a pattern syntax (i.e. no regexp, no wildcard), we
can use it to match the resource directly. Otherwise, we can use the
path within the handler mapping to resolve the resource as before.

Closes gh-29739
2023-01-20 17:15:52 +01:00
Sam Brannen 3f148c2aaa Avoid warnings / Polishing 2023-01-20 15:22:47 +01:00
Sam Brannen fa1ef1dc85 Deprecate obsolete ConcurrentExecutorAdapter for removal in 6.1
Closes gh-29865
2023-01-20 14:20:18 +01:00
Sam Brannen 58872c79ee Update copyright headers 2023-01-20 14:17:53 +01:00
Sam Brannen 24f18275dd Stop referring to "Spring 3.x" features in documentation and code 2023-01-20 14:13:18 +01:00
Sébastien Deleuze 8f94c4e933 Remove WebClientIntegrationTests#exchangeWithRelativeUrl outdated test
This test can fail when a web server runs on port 80 and
is not relevant anymore due to the removal of related feature
via 6e936a4081.

Closes gh-28902
2023-01-20 13:59:39 +01:00
Arjen Poutsma 20c79e1481 Support double-quoted in HttpHeaders::getValuesAsList
This commit introduces support for double-quoted HTTP header values in
HttpHeaders::getValuesAsList, as described in RFC 9110 section 5.5.

Closes gh-29785
2023-01-20 13:42:51 +01:00
Sébastien Deleuze fb0aa5abb3 Fix WebClientIntegrationTests flaky Jetty tests
Ensure the port used by the client in malformedResponseChunksOnBodilessEntity
and malformedResponseChunksOnEntityWithBody has correctly been set.

Closes gh-29862
2023-01-20 11:38:22 +01:00
Sam Brannen 86a40716bd Polishing 2023-01-20 10:28:25 +01:00
Sam Brannen 7f48d1b55a Remove obsolete Tiles documentation from Javadoc
See gh-29852
2023-01-20 10:27:48 +01:00
Sébastien Deleuze fd84b0997a Refine Jackson2ObjectMapperBuilder#configureFeature exception handling
This commit changes the FatalBeanException previously thrown for
an IllegalArgumentException which seems more suitable for that
use case.

Closes gh-29859
2023-01-20 10:17:17 +01:00
Juergen Hoeller 0e8838db90 Flexible constructor resolution in AutowireCapableBeanFactory.createBean(Class)
Closes gh-29855
See gh-29823
2023-01-19 17:10:42 +01:00
Sébastien Deleuze 94fc24b6fb Document that bean instance suppliers are not supported with AOT/native
Closes gh-29835
2023-01-19 16:45:52 +01:00
Sébastien Deleuze ab0d1c395d Fail explicitly in AOT transformations of instance supplier
It is by design not possible to generate code that handles
bean definitions with user-provided instance suppliers because
the JVM does not allow to get a stable reference reusable at
runtime on the lambda or method reference in the code generated
AOT.

Before this commit, such instance supplier was ignored.
After this commit, an IllegalArgumentException is thrown,
allowing projects to be aware this is not supported and enforce
related refactorings.

The related issue gh-29555 describes how this limitation could
be relaxed in the future.

Closes gh-29556
2023-01-19 16:45:52 +01:00
Sam Brannen 047f660572 Remove obsolete Tiles documentation from reference manual
Tiles support was officially removed in conjunction with gh-27423.

Closes gh-29852
2023-01-19 16:43:51 +01:00
Sam Brannen 22323072fd Polishing 2023-01-19 16:32:29 +01:00
Thom Hurks a34b1af49e Remove extra empty line in DefaultCorsProcessor (#29854) 2023-01-19 16:28:18 +01:00
Sam Brannen 1ace42f245 Enable JRubyScriptTemplateTests 2023-01-19 16:20:03 +01:00
Sam Brannen 0502d18e3d Update copyright headers 2023-01-19 16:20:03 +01:00
Sam Brannen c4c786596f Migrate to Mockito.mock(T...) where feasible 2023-01-19 16:20:02 +01:00
Sam Brannen c3d123fef7 Allow @Aspect classes to extend concrete @Aspect classes
Closes gh-29830
2023-01-19 14:35:27 +01:00
Sam Brannen e4b25ab480 Polishing 2023-01-19 14:35:27 +01:00
Arjen Poutsma 1680502313 Updated Java 17 version in sdkmanrc 2023-01-19 10:03:18 +01:00
Brian Clozel e725e3a51b Update Java versions and remove Java 18 build 2023-01-19 09:40:25 +01:00
Johnny Lim 2fe75c1a24 Polish HttpStatus 103 EARLY_HINTS javadocs (#29841)
This commit polishes the EARLY_HINTS javadocs a bit:
 - fix a typo
 - fix `since` tags to include the patch version

See gh-29816
2023-01-18 14:50:02 +01:00
Arjen Poutsma 9ebd1e8d64 Fix IllegalStateException in empty ProducesRequestCondition
When comparing empty ProducesRequestCondition, compareTo would throw an
IllegalStateException if the Accept header was invalid. This commit
fixes that behavior.

Closes gh-29794
2023-01-18 10:38:09 +01:00
Sam Brannen 64de6de725 Update copyright headers 2023-01-17 15:49:26 +01:00
Sam Brannen c3e8ff9ce7 Do not use Mockito to mock ReactiveAdapterRegistry 2023-01-17 15:44:57 +01:00
Brian Clozel 5448e81021 Deprecate Base64Utils in favor of Java's Base64
Originally, `Base64Utils` was providing base64 encoding and decoding
utilities, bridging to commons-codecs or Java 8, if available. Since
then, only the Java 8 variant remains and Spring Framework 6 requires
now Java 17. This utility class doesn't provide additional checks or
syntactic sugar over what's in Java already.

As a result, this commit deprecates this class in favor of `Base64` and
schedules the removal of this class completely.

Closes gh-28434
2023-01-17 15:30:45 +01:00
j3graham f7e07322ab Simplify Base64Utils by using java.util.Base64
This commit simplifies the `Base64Utils` implementation by relying more
on the `Base64` methods that perform similar work.

See gh-28434
2023-01-17 15:29:58 +01:00
정준영 37217a63e1 Remove unnecessary calls to Reference::clear
Prior to this commit, `ConcurrentReferenceHashMap` subclasses extending
`Reference` would call `Reference::clear` right next to
`Reference::enqueue`; this is not needed as the latter calls it already.

Closes gh-28863
2023-01-17 14:44:39 +01:00
Sam Brannen ad5c636aff Upgrade to Mockito 5.0 2023-01-17 14:31:11 +01:00
Brian Clozel ebdc82b86b Add serialization hint for ProblemDetail
Prior to this commit, a Spring web application would not be able to
serialize a `ProblemDetail` object instance in a Native Image, as
serialization hints would be missing.

This commit adds a RuntimeHints registrar that processes the
`ProblemDetail` type accordingly during the AOT phase.

Fixes gh-29801
2023-01-17 10:59:52 +01:00
Sam Brannen a12c9a0430 Update copyright headers 2023-01-16 19:24:30 +01:00
Sam Brannen 0d14c3bc1a Polish Javadoc in spring-jms 2023-01-16 19:24:30 +01:00
Sam Brannen 8be542016b Apply "instanceof pattern matching" in spring-jms 2023-01-16 19:24:30 +01:00
Arjen Poutsma 3a585f8c29 Introduce CoWebFilter
This commit introduces the CoWebFilter, an abstract WebFilter
implementation that allows for coroutine usage.

Closes gh-29672
2023-01-16 15:45:08 +01:00
Minsoo Cheong(Merlin) c0e38a2f1e Fix R2dbcTransactionManager debug log: don't log a Mono (#29800)
When logging the current connection inside R2dbcTransactionManager
doBegin, the mono object was logged instead of the connection lambda
parameter.

Other similar debug-level logs do use the actual Connection object,
so this commit does the same.
2023-01-16 11:29:43 +01:00
Simon Baslé 5de1460f88 Deprecate HttpStatus 103 CHECKPOINT in favor of new EARLY_HINTS (#29816)
This commit takes rfc8297 into account and introduces a newer code 103
HttpStatus value which uses `Early Hints` as the more correct reason
phrase, deprecating the outdated `CHECKPOINT` enum value for 103.

Additionally:
  - `HttpStatus.valueOf(103)` will return the new enum value
  - `HttpStatusCode#isSameCodeAs(HttpStatusCode)` is introduced to ease
  comparison of deprecated enums vs their newer counterparts (or any
  instance of a more generic `HttpStatusCode`) by comparing the integer
  `value()`
  - `HttpStatusTests` covers the new deprecation as well as the three
  previously deprecated codes, including a check with the above new
  method to ensure they have comparable integer values

Supersedes and Closes gh-27960
2023-01-16 11:22:43 +01:00
rstoyanchev 312db36849 Combined, empty RequestMapping matches both "" and "/"
Closes gh-29625
2023-01-13 14:58:11 +00:00
Sam Brannen 46875c91ce Update copyright year in reference manual 2023-01-11 17:32:31 +01:00
Sam Brannen 86305c45a9 Upgrade to spring-asciidoctor-backends 0.0.4 2023-01-11 17:30:02 +01:00
Sam Brannen 1342837686 Upgrade to nohttp 0.0.11 2023-01-11 17:26:35 +01:00
Sam Brannen 012b8f18c6 Add Simon to mailmap 2023-01-11 15:26:31 +01:00
Brian Clozel 5ea310452c Add Simon to the Changelog generator configuration
This prevents Simon from being listed as an external contributor.
2023-01-11 14:12:03 +01:00
Sam Brannen a4956dfe26 Update copyright headers 2023-01-11 13:52:20 +01:00
Sam Brannen 0415975dd1 Polish contribution and related code 2023-01-11 13:52:20 +01:00
Krzysztof Krason afb8a0d1b1 Use new Java features (switch expressions, text blocks, new JDK methods)
Closes gh-29747
2023-01-11 13:51:28 +01:00
Spring Builds 48abd493fe Next development version (v6.0.5-SNAPSHOT) 2023-01-11 12:25:34 +00:00
Brian Clozel 3d571e8ab5 Upgrade to Micrometer 1.10.3
Closes gh-29802
2023-01-11 11:55:43 +01:00
Juergen Hoeller c7cfdfc26c Downgrade to Mockito 4.9 for spring-beans tests
Includes upgrade to HtmlUnit 2.69
2023-01-11 00:26:30 +01:00
Juergen Hoeller 4d27aee7cf Upgrade to Checkstyle 10.6 2023-01-11 00:03:23 +01:00
Juergen Hoeller 63f0beb209 Upgrade to Reactor 2022.0.2
Includes Mockito 4.11 and AssertJ 3.24.1

Closes gh-29797
2023-01-11 00:01:34 +01:00
Sam Brannen 8dbdfb0449 Polishing 2023-01-10 16:08:02 +01:00
Sam Brannen d5fb5d029b Polish contribution 2023-01-10 16:07:48 +01:00
Vasiliy Kudriavtsev 3738a45658 Avoid wasted memory on empty maps and sets
This commit avoids wasted memory on empty hash maps in
MergedAnnotationReadingVisitor and empty sets in InjectionMetadata.

Closes gh-29742
2023-01-10 15:52:17 +01:00
Sam Brannen fd3e99c7ee Upgrade to JUnit 5.9.2 2023-01-10 15:23:20 +01:00
Sébastien Deleuze 4396801933 Add reflection hints for Kotlin reflection on functions
Kotlin reflection API invocation on a specific function
may require iterating on all Java methods to find the right
Kotlin function. As a consequence, this commit adds introspection
hints on the class declared methods for all Kotlin beans since
the impact on the footprint is low.

Closes gh-29663
2023-01-10 14:49:26 +01:00
Brian Clozel a516ed8739 Upgrade Gradle Enterprise & Conventions plugins 2023-01-10 09:10:59 +01:00
Juergen Hoeller 88cac6a4a3 Reduce creation of composite interfaces to actual method references in current pointcut
Closes gh-29519
2023-01-09 20:34:20 +01:00
Sam Brannen 11df955d88 Update copyright headers 2023-01-09 18:33:06 +01:00
Sam Brannen 77832a6da9 Apply "instanceof pattern matching" in HttpHeaders 2023-01-09 18:27:13 +01:00
Sam Brannen ce1f6cf0bf Polishing 2023-01-09 18:27:13 +01:00
rstoyanchev c702cd418e Polishing
See gh-29721
2023-01-06 16:50:19 +00:00
Simon Baslé 4bcc24372a Add ExecutingResponseCreator
This commit adds a new `ResponseCreator` implementation that uses a
`ClientHttpRequestFactory` to perform an actual request.

Closes gh-29721
2022-12-20 16:36:06 +01:00
rstoyanchev ae7cff35dc Polishing 2022-12-20 09:39:15 +00:00
IDJack 8282be682c Fix typo in core-aot.adoc
Closes gh-29788
2023-01-09 13:19:16 +01:00
Sébastien Deleuze b81a291c85 Add reflection hints for @RequestPart
Closes gh-29749
2023-01-06 17:26:33 +01:00
Sébastien Deleuze 3348e74ab8 Add native support for @Convert on JPA entities
This commit infers the reflection hints required for converters
when they are specified with the @Convert annotation at class or
field level.

It also refines the hints generated for @Converter in order
to just generate the construct hint, not the public method one
which should be not needed.

Closes gh-29771
2023-01-06 12:56:26 +01:00
Simon Baslé e2832ea596 Polish #29727: Mention Kotlin andExpectAll in reference manual (#29766)
This just adds a Kotlin snippet alongside the Java snippet in the
reference manual.

Relates to gh-29727
Closes gh-27317
2023-01-04 19:11:54 +01:00
Simon Baslé 74f58198fd Add Kotlin DSL support for MockMVC andExpectAll (#29727)
As the DSL internally calls `ResultActions.andExpect`, this is done with
a trick where a synthetic `ResultActions` is provided at top level which
stores each `ResultMatcher` in a mutable list.

Once the DSL usage is done, the top level DSL `andExpectAll` turns that
list into a `vararg` passed down to the actual `actions.andExpectAll`.

Closes gh-27317
2023-01-03 18:26:26 +01:00
Juergen Hoeller 42b16591ec SpringPersistenceUnitInfo leniently ignores transformer if no LoadTimeWeaver is present
Closes gh-29736
2023-01-03 11:55:33 +01:00
Johnny Lim 62cf2f0a4f Fix wrong asserted code in SQLExceptionSubclassTranslatorTests (#29748)
There was a typo in the test, covering the wrong SQLState code.

Polishes a644245.
Relates to gh-29699.
2023-01-03 10:57:02 +01:00
Juergen Hoeller 32e1a6452a Upgrade to Undertow 2.3.2, Apache HttpClient 5.2.1, Selenium HtmlUnit Driver 2.67 2022-12-23 16:11:16 +01:00
Juergen Hoeller 55295faff0 Upgrade to Tomcat 10.1.4, Jetty 11.0.13, Netty 4.1.86, Protobuf 3.21.12, SLF4J 2.0.6 2022-12-23 15:14:52 +01:00
Juergen Hoeller 254c3725e2 Polishing 2022-12-23 15:14:11 +01:00
Juergen Hoeller d5ff232246 Defensive check for null returned from createConnection()
Closes gh-29706
2022-12-23 15:13:41 +01:00
Juergen Hoeller a644245e0e Check well-known database error codes in case of generic SQL state 23000
Closes gh-29699
2022-12-23 15:13:29 +01:00
Sam Brannen 697292ba0c Apply update_copyright_headers.sh 2022-12-20 15:21:54 +01:00
Brian Clozel 5366ac84e6 Fix path within mapping when pattern contains ".*"
Prior to this commit, extracting the path within handler mapping would
result in "" if the matching path element would be a Regex and contain
".*". This could cause issues with resource handling if the handler
mapping pattern was similar to `"/folder/file.*.extension"`.

This commit introduces a new `isLiteral()` method in the `PathElement`
abstract class that expresses whether the path element can be compared
as a String for path matching or if it requires a more elaborate
matching process.

Using this method for extracting the path within handler mapping avoids
relying on wildcard count or other properties.

Fixes gh-29712
2022-12-19 10:15:39 +01:00
Sam Brannen b71db12c43 Apply "instanceof pattern matching" in spring-aop 2022-12-18 14:14:40 +01:00
Sam Brannen ab571e3562 Revert incorrect change to ParamAware Javadoc 2022-12-18 14:14:40 +01:00
Sam Brannen 5e42a6eb0a Update Trigger & TriggerContext reference documentation
Closes gh-29702
2022-12-18 13:21:27 +01:00
Sam Brannen 0bfddbc9cb Polishing 2022-12-18 13:20:46 +01:00
diguage 511dab1ade Apply "instanceof pattern matching" (#29710) 2022-12-18 12:46:15 +01:00
Sam Brannen d4623a393b Update copyright headers 2022-12-18 12:16:19 +01:00
Sam Brannen c2c3be4ee3 Polishing 2022-12-18 12:05:24 +01:00
Sam Brannen 270f8526d1 Fix typos in anchors 2022-12-18 12:05:24 +01:00
Sam Brannen 4171e35546 Fix formatting in examples 2022-12-18 12:05:18 +01:00
Sam Brannen bea505d32e Fix typos in reference manual 2022-12-18 12:05:18 +01:00
Sam Brannen 5c28b56823 Remove duplicated words in Javadoc 2022-12-18 12:05:18 +01:00
Sam Brannen d2b54e2611 Remove duplicate words in reference manual
Found using regular expression: \b(\w+)\s+\1\b
2022-12-18 10:46:30 +01:00
Sam Brannen f8a58f2bc4 Update Jakarta Mail info in ref docs
Closes gh-29707
2022-12-17 14:24:38 +01:00
Sam Brannen f6e1fb3150 Cross reference WebTestClient section 2022-12-17 14:01:21 +01:00
Simon Baslé 14c7b9bd08 Change plain 'WebMVC' links to 'See equivalent in the Servlet stack'
Closes gh-29694
2022-12-17 13:51:29 +01:00
Sam Brannen 11f3edc352 Change plain 'WebFlux' links to 'See equivalent in the Reactive stack'
Closes gh-29694
2022-12-17 13:51:29 +01:00
Simon Baslé fbd4463f20 Fix subsection style in WebFlux Concurrency Model
The block title style previously used was not rendered in HTML and the
title couldn't be differentiated from the text. Though, it was in the
PDF, as italics.

Introducing delimited blocks in the open (`--`) style did introduce
styling, but the vertical alignment isn't great.

This commit turns these block titles to actual (deep) section titles.
In the final HTML, at this depth there is no numbering but bold styling
is there. The PDF rendering has also been verified to have relevant
style.

Closes gh-29694
2022-12-17 13:43:08 +01:00
Simon Baslé a3a94950f6 Rework linking to Spring MVC Async support vs WebFlux section
The link was previously named "Compared to WebFlux", which is easy to
mix up with the various links to equivalent sections in the WebFlux
chapter. Here the links point to a small section comparing the Servlet
Async API to the WebFlux stack from a high perspective.

In this commit we eliminate most of these links, except at the
beginning of the Asynchronous section. We also add a small mention of
the Servlet configuration in the comparison paragraphs, since the
Configuring section is the one furthest from the comparison paragraphs
that used to have a link to it.

Closes gh-29694
2022-12-17 13:43:04 +01:00
Simon Baslé b51a14ff92 Remove statement that users could be on Java < 5
Closes gh-29694
2022-12-17 13:42:59 +01:00
Simon Baslé 929d5567b8 Remove ref to JOTM, inactive since 2009
Closes gh-29694
2022-12-17 13:42:55 +01:00
Simon Baslé beaa7ba0e3 Fix link to Jakarta Mail
Closes gh-29694
2022-12-17 13:42:50 +01:00
Simon Baslé 1a839f5103 Fix typos in reference manual
Closes gh-29694
2022-12-17 13:42:46 +01:00
Sam Brannen 8df4f927fd Polish contribution
See gh-29697
2022-12-17 11:10:44 +01:00
Simon Baslé 5965917d16 Extract ResourceEntityResolver HTTPS schema resolution fallback
This commit extracts the DTD/XSD remote lookup fallback from the
resolveEntity() method into a protected method.

A WARN-level logging statement is added to the extracted fallback in
order to make it clear that remote lookup happened.

Overriding the protected method would allow users to avoid this
fallback entirely if it isn't desirable, without the need to duplicate
the local resolution code.

Closes gh-29697
2022-12-17 10:34:15 +01:00
Sam Brannen 57cfb94f1f Improve documentation for literals in SpEL expressions
Closes gh-29700
2022-12-16 14:50:07 +01:00
Sam Brannen b5b80d0a07 Polishing 2022-12-16 14:50:02 +01:00
Sam Brannen e5cfbae3fd Polishing 2022-12-16 13:48:45 +01:00
Carlos Belizón 5d2ca11315 Fix manipulating property sources example in Javadoc for ConfigurableEnvironment
The "manipulating property sources" example in the Javadoc for
`ConfigurableEnvironment` states that `MutablePropertySources`
expect a `Map<String,String>`; whereas it expects a
`Map<String,Object>`.

Closes gh-29693
2022-12-16 13:47:40 +01:00
Spring Builds 858394b720 Next development version (v6.0.4-SNAPSHOT) 2022-12-15 09:12:22 +00:00
Sam Brannen 6e08c56076 Improve Javadoc for RepeatableContainers 2022-12-14 13:40:43 +01:00
Sam Brannen fb6d3f5b5c Remove duplicated test code 2022-12-14 13:26:51 +01:00
Arjen Poutsma 6fe5652783 Support non-standard HTTP methods in FrameworkServlet
This commit ensures that HTTP methods not supported by HttpServlet, for
instance WebDAV methods, are still supported in FrameworkServlet.

Closes gh-29689
2022-12-14 12:54:41 +01:00
Brian Clozel ca68bbc9ff Upgrade to Reactor 2022.0.1
Closes gh-29679
2022-12-13 22:52:24 +01:00
Sam Brannen e7bcb48e57 Remove obsolete AttributeMethods.hasOnlyValueAttribute() method
See gh-29685
2022-12-13 16:01:05 +01:00
Sam Brannen 433b1c480c Support repeatable annotation containers with multiple attributes
Prior to this commit, there was a bug in the implementation of
StandardRepeatableContainers.computeRepeatedAnnotationsMethod() which
has existed since Spring Framework 5.2 (when
StandardRepeatableContainers was introduced). Specifically,
StandardRepeatableContainers ignored any repeatable container
annotation if it declared attributes other than `value()`. However,
Java permits any number of attributes in a repeatable container
annotation.

In addition, the changes made in conjunction with gh-20279 made the bug
in StandardRepeatableContainers apparent when using the
getMergedRepeatableAnnotations() or findMergedRepeatableAnnotations()
method in AnnotatedElementUtils, resulting in regressions for the
behavior of those two methods.

This commit fixes the regressions and bug by altering the logic in
StandardRepeatableContainers.computeRepeatedAnnotationsMethod() so that
it explicitly looks for the `value()` method and ignores any other
methods declared in a repeatable container annotation candidate.

Closes gh-29685
2022-12-13 16:00:55 +01:00
Sam Brannen 0b08246760 Revise RepeatableContainersTests 2022-12-13 15:54:43 +01:00
Sam Brannen c7bdfbea4f Add missing Javadoc
See gh-29574
2022-12-13 13:46:31 +01:00
Sam Brannen 618989d317 Update copyright date 2022-12-13 13:44:18 +01:00
Sam Brannen 7fe78b745f Polish Javadoc 2022-12-13 13:44:03 +01:00
Juergen Hoeller 98f152e8d5 Polishing 2022-12-13 11:07:46 +01:00
Juergen Hoeller d74191427e Avoid NPE on BeanDescriptor access with SimpleBeanInfoFactory
Closes gh-29681
2022-12-13 11:07:22 +01:00
Juergen Hoeller 4c69892f39 Detect SQL state 23505/40001 as DuplicateKeyException/CannotAcquireLockException
Favors PessimisticLockingFailureException over plain ConcurrencyFailureException.
Deprecates CannotSerializeTransactionException and DeadlockLoserDataAccessException.

Closes gh-29511
Closes gh-29675
2022-12-13 11:07:13 +01:00
Arjen Poutsma c79ae0c842 Align multipart codecs on client and server
This commit ensures that the same multipart codecs are registered on
both client and server. Previously, only the client enabled only sending
 multipart, and the server only receiving.

Closes gh-29630
2022-12-13 10:13:46 +01:00
Sam Brannen 46fc28fd1a Clean up Javadoc and source code regarding " ." typos 2022-12-12 16:31:14 +01:00
Johnny Lim f8a1dac593 Use consistent visibility for ResponseEntityExceptionHandler.getMessageSource()
See gh-29574
Closes gh-29676
2022-12-12 16:15:52 +01:00
Brian Clozel 9712bb62fd Upgrade optional dependencies 2022-12-12 14:58:21 +01:00
Brian Clozel e85f6342a2 Start building against Reactor 2022.0.1
See gh-29679
2022-12-12 14:58:21 +01:00
Brian Clozel 7199782d45 Upgrade to Micrometer 1.10.2
Closes gh-29678
2022-12-12 14:58:13 +01:00
Sébastien Deleuze 5ed4a14e2b Make @ModelAttribute and @InitBinder reflective
Closes gh-29572
2022-12-11 12:02:55 +01:00
Sébastien Deleuze b27735d4a6 Refine BindingReflectionHintsRegistrar Kotlin support
Closes gh-29593
2022-12-11 12:02:55 +01:00
Sébastien Deleuze dc5a773b2b Support properties on records in BindingReflectionHintsRegistrar
Closes gh-29571
2022-12-10 16:12:23 +01:00
Sam Brannen 2b6f3caff4 Remove dead code in MockClientHttpRequest 2022-12-09 15:57:35 -05:00
Sam Brannen 83eb8ac0ea Introduce additional constructors in MockClientHttp[Request|Response]
This commit introduces additional constructors in MockClientHttpRequest
and MockClientHttpResponse that were previously only present in the
internal "test fixtures" in spring-web.

This commit also aligns the mocks in spring-test with the test fixtures
in spring-web to simplify continued maintenance of the mocks and test
fixtures.

Closes gh-29670
2022-12-09 15:46:08 -05:00
Sam Brannen 9b38e43c17 Use URI#create instead of URI constructor where feasible in spring-websocket 2022-12-09 13:27:34 -05:00
Sam Brannen 284cb12f8f Use URI#create instead of URI constructor where feasible in spring-webmvc 2022-12-09 13:27:13 -05:00
Sam Brannen 67644a28b6 Use URI#create instead of URI constructor where feasible in spring-webflux 2022-12-09 13:26:30 -05:00
Sam Brannen 1b57f2bda5 Use URI#create instead of URI constructor where feasible in spring-web 2022-12-09 13:26:01 -05:00
Sam Brannen a1a140f7d5 Use URI#create instead of URI constructor where feasible in spring-test 2022-12-09 12:27:06 -05:00
Sam Brannen 4caf3c8ce0 Polish tests in spring-test 2022-12-09 12:26:38 -05:00
Sam Brannen 485c80fcf3 Apply 'instanceof pattern matching' in spring-test and Servlet mocks 2022-12-09 11:56:14 -05:00
rstoyanchev aae46263cc Restore top-level read permission for backport bot 2022-12-09 12:02:56 +00:00
rstoyanchev 39d4d2041a Polishing contribution
Closes gh-29634
2022-12-09 10:24:42 +00:00
koo.taejin 5ac97b16a8 Optimize object creation PartialMatchHelper
See gh-29634
2022-12-09 10:19:54 +00:00
rstoyanchev f457c17c78 Remove top-level permissions from backport-bot.yml 2022-12-09 09:10:03 +00:00
Sam Brannen b0b2bbc857 Polish CGLIB fork
- primarily automated "clean up" using Eclipse IDE
2022-12-09 00:56:38 -05:00
Sam Brannen 69f47e7700 Polishing
- primarily automated "clean up" using Eclipse IDE
2022-12-09 00:56:00 -05:00
Sam Brannen 52af5c2b38 Support arrays in AST string representations of SpEL expressions
Prior to this commit, SpEL's ConstructorReference did not provide
support for arrays when generating a string representation of the
internal AST. For example, 'new String[3]' was represented as 'new
String()' instead of 'new String[3]'.

This commit introduces support for standard array construction and array
construction with initializers in ConstructorReference's toStringAST()
implementation.

Closes gh-29665
2022-12-08 18:17:45 -05:00
Sam Brannen 404661d5cb Polishing 2022-12-08 16:03:05 -05:00
Sam Brannen f07a4587bb Apply 'instanceof pattern matching' in spring-expression 2022-12-07 17:15:25 -05:00
Sam Brannen 114d6a9256 Apply 'instanceof pattern matching' in spring-jdbc 2022-12-07 17:15:25 -05:00
Sam Brannen 73a18046c3 Fix SpEL support for quotes within String literals
Prior to this commit, there were two bugs in the support for quotes
within String literals in SpEL expressions.

- Two double quotes ("") or two single quotes ('') were always replaced
  with one double quote or one single quote, respectively, regardless
  of which quote character was used to enclose the original String
  literal. This resulted in the loss of one of the double quotes when
  the String literal was enclosed in single quotes, and vice versa. For
  example, 'x "" y' became 'x " y'.

- A single quote which was properly escaped in a String literal
  enclosed within single quotes was not escaped in the AST string
  representation of the expression. For example, 'x '' y' became 'x ' y'.

This commit fixes both of these related issues in StringLiteral and
overhauls the structure of ParsingTests.

Closes gh-29604, gh-28356
2022-12-07 15:43:47 -05:00
Sam Brannen c899af0e03 Polishing 2022-12-07 15:43:47 -05:00
Sam Brannen 098c924e32 Introduce @Suite classes for individual modules 2022-12-07 12:12:41 -05:00
Sam Brannen 4289a64e24 Improve Javadoc for SqlLobValue 2022-12-07 12:12:41 -05:00
Sam Brannen 7615762039 Update copyright headers 2022-12-07 12:11:45 -05:00
Brian Clozel d601f3196a Fix BindingReflectionHintsRegistrar anonymous classes support
This commit ensures that giving an anonymous class for reflection hints
registration does not result in a NullPointerException, since the
canonical name of anonymous classes is null.

Fixes gh-29657
2022-12-07 16:37:01 +01:00
Arjen Poutsma 92a6e7ddcd Fix InputStream violation in DataBufferInputStream
This commit fixes an issue in DataBufferInputStream::mark, which before
did not follow the contract defined by InputStream.

Closes gh-29642
2022-12-07 15:23:19 +01:00
ShenFeng312 c79474d269 Polish var-args declarations
Closes gh-29640
2022-12-07 04:30:40 +01:00
Sam Brannen f4bc9ffb98 Reintroduce component index support for Jakarta annotations
Spring Framework 6.0 GA introduced a regression in the component index
support for Jakarta annotations such as @Named and @ManagedBean.

Prior to this commit, @Named and @ManagedBean components were
registered in the component index at build time; however, component
scanning failed to find those component at run time.

This commit updates ClassPathScanningCandidateComponentProvider so that
`jakarta.*` annotation types are once again supported for component
scanning via the component index at run time.

Closes gh-29641
2022-12-06 16:23:31 -05:00
Sam Brannen e124e802a3 Polishing 2022-12-06 16:23:31 -05:00
Sam Brannen 348cc01284 Rename MultipartWebClientIntegrationTests classes to avoid duplicate names 2022-12-06 12:50:36 -05:00
Sam Brannen 9f7a510f90 Polishing 2022-12-06 12:29:03 -05:00
divcon f64397d822 Apply "instanceof pattern matching" in spring-webflux
Closes gh-29635
2022-12-06 10:03:26 -05:00
rstoyanchev 918edaba2e Add convenience methods for binding error messages
Closes gh-29574
2022-12-06 13:37:08 +00:00
rstoyanchev 5214bd3093 Fix issue with getHeaders in NoHandlerFoundException
Closes gh-29626
2022-12-06 13:37:08 +00:00
Moritz Halbritter 752ef6f456 Fall back to JdkClientHttpConnector as ClientHttpConnector 2022-12-06 14:33:03 +01:00
rstoyanchev bea1980e22 Correct (again) permissions key in backport bot config 2022-12-06 09:41:05 +00:00
rstoyanchev f3360e4ee2 Correct permissions key in backport bot config 2022-12-06 09:38:13 +00:00
rstoyanchev 8330bd424b Add write permission in backport bot config 2022-12-06 09:34:07 +00:00
rstoyanchev 507c9697fb Correct event name in backport bot config 2022-12-06 09:27:03 +00:00
rstoyanchev 52c073724d Enable backport bot for pull requests 2022-12-06 09:13:29 +00:00
Sam Brannen de07ad47f1 Polishing, suppression of deprecation warnings, copyright headers, etc. 2022-12-05 14:16:22 -05:00
rstoyanchev 93ea2e1df9 Push canWrite down into MultipartHttpMessageWriter
The implementation in the base class only matches the
MultipartHttpMessageWriter subclass. The other two override it anyway.

Closes gh-29631
2022-12-05 15:37:48 +00:00
CoderYellow a7bf14b364 Fix canWrite of PartHttpMessageWriter
See gh-29631
2022-12-05 15:37:48 +00:00
rstoyanchev 7899fa3b0a Polishing contribution
Closes gh-29622
2022-12-05 15:37:48 +00:00
Marten Deinum c1da2a4e73 Improve Netty code sample
See gh-29622
2022-12-05 15:37:48 +00:00
rstoyanchev db53b618c1 Simplify form data handling in HttpRequestValues
Closes gh-29615
2022-12-05 15:37:48 +00:00
rstoyanchev 913163884a Improve invalid Content-Type handling in WebFlux
Closes gh-29565
2022-12-05 15:37:48 +00:00
Brian Clozel 4a555aaef1 Reduce heap memory usage in ConcurrentLruCache
Prior to this commit, the `ConcurrentLruCache` implementation would use
arrays of `AtomicReference` as operation buffers, and the buffer count
would be calculated with the nearest power of two for the CPU count.

This can result in significant heap memory usage as each
`AtomicReference` buffer entry adds to the memory pressure. As seen in
FasterXML/jackson-databind#3665, this can add a significant overhead for
no real added benefit for the current use case.

This commit changes the current implementation to use
`AtomicReferenceArray` as buffers and reduce the number of buffers.
JMH benchmarks results are within the error margin so we can assume that
this does not change the performance characteristics for the typical use
case in Spring Framework.

Fixes gh-29520
2022-12-05 13:02:11 +01:00
Brian Clozel e9ada8d15c Upgrade JMH build plugins
This commit upgrades the Gradle JMH plugin to 0.6.8 and the companion
JMH version to 1.36.
2022-12-05 13:02:11 +01:00
Sam Brannen 63d841664c Polish ref docs build 2022-12-04 22:35:04 -05:00
Sam Brannen 9779d25d98 Revise PDF ref docs to include TOC, authors, and legal sections 2022-12-04 22:13:20 -05:00
Sam Brannen 89d0c6b962 Do not refer to HTML ref docs from HTML ref docs 2022-12-04 22:11:41 -05:00
Sam Brannen 20da0835b3 Polish overview for consistency 2022-12-04 12:33:44 -05:00
Sam Brannen 4f232a9003 Update copyright headers 2022-12-03 18:24:44 -05:00
Sam Brannen c24a51323d Reinstate test for JmxUtils.locateMBeanServer() 2022-12-03 18:23:41 -05:00
Sam Brannen 69d022712b Polish Javadoc for ErrorResponse etc. 2022-12-03 16:19:33 -05:00
Sam Brannen aa1f3d1681 Apply "instanceof pattern matching" 2022-12-03 16:06:58 -05:00
Spark61 b366cd352e Fix ErrorResponse#type documentation
Closes gh-29632
2022-12-03 22:06:16 +01:00
Juergen Hoeller ac667a1e2b Upgrade to Apache HttpClient 5.2
Includes JRuby 9.4, Groovy 4.0.6, Hibernate ORM 5.6.14, HSQLDB 2.7.1, SLF4J 2.0.5, Caffeine 3.1.2, Gson 2.10, POI 5.2.3, Protobuf 3.21.10, WebJars Locator 0.52, HtmlUnit 2.67, Mockito 4.9, Checkstyle 10.5

Closes gh-29627
2022-12-02 20:57:35 +01:00
rstoyanchev a09f93768a Fix URI override for HttpExchange
Closes gh-29624
2022-12-02 17:02:05 +00:00
Brian Clozel 0ccd2f8b87 Deprecate GraphQL media type in favor of new one
This commit deprecates the `"application/graphql+json"` media type in
favor of the new `"application/graphql-response+json"`, since the former
has been removed in graphql/graphql-over-http#215.

Closes gh-29617
2022-12-02 16:25:59 +01:00
Baljit Singh b1fdb148d0 ResponseStatusException delegates to protected constructor
This ensures that by default the reason is used to set the "detail"
field. It's a follow-up fix to a27f2e994b
which resolved the issue partially.

Closes gh-29608
2022-12-02 14:20:16 +00:00
Juergen Hoeller 955ca4d146 Revised support for Jetty 12 (tested against 12.0.0.alpha2)
Avoids HttpFields optimization completely, relying on Servlet header access instead.
ServletServerHttpResponse provides applyHeaders/adaptHeaders split for better reuse.

See gh-29575
2022-12-01 19:45:40 +01:00
Juergen Hoeller d5732fed45 Polishing 2022-12-01 17:46:31 +01:00
Juergen Hoeller 4c7b04e32a Use resolved factory method return type for supplier code generation
Closes gh-29598
2022-12-01 17:46:23 +01:00
Johnny Lim 74ec10bf3a Polish
Closes gh-29619
2022-12-01 17:34:33 +01:00
Juergen Hoeller dd6cb1b728 Polishing 2022-12-01 15:48:24 +01:00
Juergen Hoeller 1e4c10cef1 Add equals/hashCode methods to ProblemDetail
Closes gh-29606
2022-12-01 15:47:40 +01:00
Juergen Hoeller e47978e168 Avoid unnecessary parameter name inspection for constructor-arg resolution
Closes gh-29612
2022-12-01 15:47:12 +01:00
Sam Brannen 52e967a525 Apply update_copyright_headers.sh to staged files as well 2022-11-30 15:18:11 +01:00
Sam Brannen 1de9d123a5 Update Javadoc for MBeanTestUtils 2022-11-30 15:15:51 +01:00
Sam Brannen ad60164911 Update copyright headers for source code changed since August 2022
The changes in this commit were performed using the newly introduced
update_copyright_headers.sh script.
2022-11-29 19:38:42 +01:00
Sam Brannen c49b0825f9 Introduce update_copyright_headers.sh shell script
In order to automate maintenance of copyright headers in our source
code (especially when merging PRs from external contributors), this
commit introduces an update_copyright_headers.sh script (tested on
mac OS) that will update the copyright headers of all Java, Kotlin,
and Groovy source files that have been added or modified this year
(or at least as far back as the git log history supports it).

For example, running this script currently outputs the following.

Updating copyright headers in Java, Kotlin, and Groovy source code for year 2022
warning: log for 'main' only goes back to Tue, 16 Aug 2022 16:24:55 +0200
2022-11-29 19:34:11 +01:00
Sam Brannen fdec9f0adc Apply "instanceof pattern matching" 2022-11-29 18:09:42 +01:00
Sam Brannen a88dbbec98 Apply project formatting rules for ternary operator
Discovered via RegEx: ^\s+\?
2022-11-29 17:51:13 +01:00
Sam Brannen 61d8774da0 Add missing callout 2022-11-29 13:54:46 +01:00
Sam Brannen c2b16f1ffb Fix code example callouts in reference manual 2022-11-29 13:54:10 +01:00
Sam Brannen 6af94dffe5 Add "missing" callout for parity 2022-11-29 13:52:26 +01:00
Sam Brannen 2147175898 Polishing 2022-11-29 13:51:34 +01:00
Sam Brannen 052542254a Polishing 2022-11-28 17:51:18 +01:00
Sam Brannen 1bb723b9e0 Rename to AbstractReactiveWebSocketIntegrationTests to avoid duplicate class names 2022-11-28 17:43:39 +01:00
Sam Brannen db02c96c55 Avoid deprecation warnings in tests 2022-11-28 17:40:46 +01:00
Sam Brannen 164141a63e Fix broken tests in SockJsServiceTests
See gh-29594
2022-11-28 16:52:15 +01:00
Sam Brannen 0a731413de Polishing 2022-11-28 16:45:38 +01:00
Sam Brannen 6e5dd64025 Polish contribution
See gh-29594
2022-11-28 16:42:22 +01:00
Aashay Chapatwala 650875468c Add title to SockJS iFrames for accessibility compliance
Closes gh-29594
2022-11-28 16:33:41 +01:00
Yanming Zhou 6d2a25b877 Fix typo in observability documentation
Closes gh-29590
2022-11-28 15:13:03 +01:00
Sam Brannen 2847621928 Fix errors in Testing chapter
- group code example callouts to ensure callouts are displayed for the
  correct examples

- add missing callouts

- fix syntax, annotation attribute names, etc.
2022-11-28 14:47:56 +01:00
Sam Brannen 3afc6a5079 Polish Testing chapter 2022-11-28 14:45:46 +01:00
rstoyanchev 6c8fb6c204 Add MessageSource getters
See gh-29574
2022-11-28 10:59:57 +00:00
Sam Brannen 8e9a553517 Update Javadoc regarding JUnit versions 2022-11-26 16:48:03 +01:00
Sam Brannen 537d539b1d Use JUnit Jupiter annotations as examples in TestContextManager JavaDoc 2022-11-26 16:17:01 +01:00
Sam Brannen 476b9cfc99 Polish TestContextManager internals 2022-11-26 16:17:01 +01:00
Sam Brannen 352087a637 Forbid loading of test ApplicationContext in AOT mode if AOT processing failed
Prior to this commit, if AOT processing of a test's ApplicationContext
failed, the TestContext Framework (TCF) still attempted to load the
context via standard (JVM) mechanisms when running in AOT mode.

For example, if a test class uses Spring Boot's @MockBean, AOT
processing of that test's context will fail with a WARN log message,
and there will no mapping from that test class to an AOT-generated
ApplicationContextInitializer (ACI). Consequently, when the test suite
is run in AOT mode that particular test class will currently fail with
a confusing stack trace due to the fact that Spring Boot's
SpringApplication attempts to locate a "main" ACI instead of the
missing "test" ACI (missing because it was not generated during AOT
processing).

In general, the TCF should not attempt to load an ApplicationContext in
"JVM mode" while running in "AOT mode".

This commit therefore reworks the logic in
DefaultCacheAwareContextLoaderDelegate to fail fast (with a meaningful
error message) if an AOT-generated ACI cannot be found while running in
AOT mode. This avoids the aforementioned confusion when @MockBean tests
fail in AOT mode (on the JVM or within a native image), and it also
helps to diagnose build problems if AOT processing has not yet been
performed for the project's test suite.

Closes gh-29579
2022-11-26 15:32:16 +01:00
Sam Brannen 33d33802a8 Improve logging in TestContextManager 2022-11-26 15:30:50 +01:00
Sam Brannen 9ec937c843 Remove erroneous Javadoc link 2022-11-26 14:40:49 +01:00
Juergen Hoeller 8e5eb84da1 Consistently register CGLIB hints for lazy resolution proxy classes
Core JDK/CGLIB proxy registration code extracted to ClassHintUtils.

Closes gh-29584
2022-11-26 13:38:34 +01:00
Sam Brannen 902f8dd1d8 Upgrade Gradle wrapper
See gh-29583
2022-11-25 23:12:08 +01:00
Juergen Hoeller 22f8ea61fb Upgrade to Gradle 7.6
Closes gh-29583
2022-11-25 22:33:09 +01:00
Brian Clozel 8ce1f14554 Document Observability support in reference docs
Closes gh-29524
2022-11-25 17:33:11 +01:00
Brian Clozel 9a3cb628af Split integration chapter in smaller documents
This commit splits the integration chapter of the reference
documentation in smaller documents for easier maintenance.
2022-11-25 17:33:07 +01:00
Brian Clozel b0a4c5d72c Fix unrendered titles in websocket section
This commit fixes the rendering of titles in the websocket section of
the reference documentation.

Fixes gh-29569
2022-11-25 17:33:03 +01:00
Brian Clozel 8b110c9971 Reinstate checkstyle for reference docs code snippets
This commit also ensures that checks are performed before the
application is rendered to get early feedback on errors.
2022-11-25 17:32:59 +01:00
Juergen Hoeller 21d3a7529c Consistent documentation references to Jakarta WebSocket (2.1)
Closes gh-29581
2022-11-25 17:07:30 +01:00
Juergen Hoeller 9c7b471634 Deprecate JettyWebSocketClient in favor of StandardWebSocketClient
JettyWebSocketClient only supported on Jetty 11, to be phased out.

Closes gh-29576
2022-11-25 17:06:15 +01:00
Juergen Hoeller 45d45c2989 Early support for Jetty 12 (developed against 12.0.0.alpha2)
Reflective getHeaders calls to be revisited; see GitHub issue #8938 in Jetty project.
HttpOutput optimization commented out still in order to avoid alpha build dependency.

See gh-29575
2022-11-25 17:04:48 +01:00
Spring Builds 4ff18741fd Next development version (v6.0.3-SNAPSHOT) 2022-11-24 13:50:54 +00:00
2975 changed files with 110331 additions and 89049 deletions
+3
View File
@@ -3,6 +3,8 @@ name: Backport Bot
on:
issues:
types: [labeled]
pull_request:
types: [labeled]
push:
branches:
- '*.x'
@@ -13,6 +15,7 @@ jobs:
permissions:
contents: read
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
+32
View File
@@ -0,0 +1,32 @@
name: Deploy Docs
on:
push:
branches-ignore: [ gh-pages ]
tags: '**'
repository_dispatch:
types: request-build-reference # legacy
schedule:
- cron: '0 10 * * *' # Once per day at 10am UTC
workflow_dispatch:
permissions:
actions: write
jobs:
build:
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects'
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: docs-build
fetch-depth: 1
- name: Dispatch (partial build)
if: github.ref_type == 'branch'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }}
- name: Dispatch (full build)
if: github.ref_type == 'tag'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD)
+3 -1
View File
@@ -29,7 +29,7 @@ spring-test/test-output/
# Maven artifacts
pom.xml
target/
/target/
# Eclipse artifacts, including WTP generated manifests
bin
@@ -50,3 +50,5 @@ atlassian-ide-plugin.xml
# VS Code
.vscode/
cached-antora-playbook.yml
+2
View File
@@ -30,5 +30,7 @@ Andy Clement <aclement@vmware.com> <andrew.clement@gmail.com>
Sam Brannen <sbrannen@vmware.com>
Sam Brannen <sbrannen@vmware.com> <sbrannen@pivotal.io>
Sam Brannen <sbrannen@vmware.com> <sam@sambrannen.com>
Simon Basle <sbasle@vmware.com>
Simon Baslé <sbasle@vmware.com>
<dmitry.katsubo@gmail.com> <dmitry.katsubo@gmai.com>
Nick Williams <nicholas@nicholaswilliams.net>
+1 -1
View File
@@ -1,3 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=17.0.5-librca
java=17.0.7-librca
+1 -1
View File
@@ -1,4 +1,4 @@
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-5.3.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x?groups=Build") [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-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://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-6.0.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x?groups=Build") [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-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".
+5
View File
@@ -1,5 +1,10 @@
# 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
+25 -31
View File
@@ -1,16 +1,14 @@
plugins {
id 'io.spring.nohttp' version '0.0.10'
id 'io.freefair.aspectj' version '6.5.0.3' apply false
id 'io.spring.nohttp' version '0.0.11'
id 'io.freefair.aspectj' version '8.0.1' apply false
// kotlinVersion is managed in gradle.properties
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
id 'org.jetbrains.dokka' version '1.7.20'
id 'org.asciidoctor.jvm.convert' version '3.3.2' apply false
id 'org.asciidoctor.jvm.pdf' version '3.3.2' apply false
id 'org.jetbrains.dokka' version '1.8.10'
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
id 'com.github.ben-manes.versions' version '0.42.0'
id 'com.github.johnrengelman.shadow' version '7.1.2' apply false
id 'de.undercouch.download' version '5.1.0'
id 'me.champeau.jmh' version '0.6.6' apply false
id 'com.github.ben-manes.versions' version '0.46.0'
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
id 'de.undercouch.download' version '5.4.0'
id 'me.champeau.jmh' version '0.7.1' apply false
}
ext {
@@ -28,7 +26,6 @@ configure(allprojects) { project ->
includeGroup 'io.projectreactor.netty'
}
}
maven { url "https://repo.spring.io/libs-spring-framework-build" }
if (version.contains('-')) {
maven { url "https://repo.spring.io/milestone" }
}
@@ -78,7 +75,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "10.4"
toolVersion = "10.12.1"
configDirectory.set(rootProject.file("src/checkstyle"))
}
@@ -105,42 +102,38 @@ configure([rootProject] + javaProjects) { project ->
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-slf4j-impl")
testRuntimeOnly("org.apache.logging.log4j:log4j-slf4j2-impl")
// JSR-305 only used for non-required meta-annotations
compileOnly("com.google.code.findbugs:jsr305")
testCompileOnly("com.google.code.findbugs:jsr305")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.31")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.38")
}
ext.javadocLinks = [
"https://docs.oracle.com/en/java/javase/17/docs/api/",
"https://jakarta.ee/specifications/platform/9/apidocs/",
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ
"https://www.ibm.com/docs/api/v1/content/SSEQTP_8.5.5/com.ibm.websphere.javadoc.doc/web/apidocs/",
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/",
"https://docs.jboss.org/jbossas/javadoc/7.1.2.Final/",
"https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/",
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ and weblogic.* packages
"https://www.ibm.com/docs/api/v1/content/SSEQTP_8.5.5/com.ibm.websphere.javadoc.doc/web/apidocs/", // com.ibm.*
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/", // org.jboss.resource.*
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
"https://eclipse.dev/aspectj/doc/released/aspectj5rt-api",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://fasterxml.github.io/jackson-core/javadoc/2.10/",
"https://fasterxml.github.io/jackson-databind/javadoc/2.10/",
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.10/",
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.14.1/",
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind/2.14.1/",
"https://www.javadoc.io/doc/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.14.1/",
"https://hc.apache.org/httpcomponents-client-5.2.x/current/httpclient5/apidocs/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.2/",
// TODO Uncomment link to JUnit 5 docs once we have sorted out
// the following warning in the build.
// TODO Uncomment link to JUnit 5 docs once we execute Gradle with Java 18+.
// See https://github.com/spring-projects/spring-framework/issues/27497
//
// warning: The code being documented uses packages in the unnamed module, but the packages defined in https://junit.org/junit5/docs/5.9.1/api/ are in named modules.
//
// "https://junit.org/junit5/docs/5.9.1/api/",
// "https://junit.org/junit5/docs/5.9.3/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/",
// The external Javadoc link for JSR 305 must come last to ensure that types from
// JSR 250 (such as @PostConstruct) are still supported. This is due to the fact
// that JSR 250 and JSR 305 both define types in javax.annotation, which results
// in a split package, and the javadoc tool does not support split packages
// across multiple external Javadoc sites.
// Previously there could be a split-package issue between JSR250 and JSR305 javax.annotation packages,
// but since 6.0 JSR 250 annotations such as @Resource and @PostConstruct have been replaced by their
// JakartaEE equivalents in the jakarta.annotation package.
"https://www.javadoc.io/doc/com.google.code.findbugs/jsr305/3.0.2/"
] as String[]
}
@@ -157,6 +150,7 @@ configure(rootProject) {
nohttp {
source.exclude "**/test-output/**"
source.exclude "**/.gradle/**"
allowlistFile = project.file("src/nohttp/allowlist.lines")
def rootPath = file(rootDir).toPath()
def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/buildSrc"
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,8 +26,8 @@ import org.gradle.testretry.TestRetryTaskExtension;
* Conventions that are applied in the presence of the {@link JavaBasePlugin}. When the
* plugin is applied:
* <ul>
* <li>The {@link TestRetryPlugin Test Retry} plugins is applied so that flaky tests
* are retried 3 times when running on the CI.
* <li>The {@link TestRetryPlugin Test Retry} plugin is applied so that flaky tests
* are retried 3 times when running on the CI server.
* </ul>
*
* @author Brian Clozel
@@ -40,9 +40,8 @@ class TestConventions {
}
private void configureTestConventions(Project project) {
project.getPlugins().apply(TestRetryPlugin.class);
project.getTasks().withType(Test.class,
(test) -> project.getPlugins().withType(TestRetryPlugin.class, (testRetryPlugin) -> {
test -> project.getPlugins().withType(TestRetryPlugin.class, testRetryPlugin -> {
TestRetryTaskExtension testRetry = test.getExtensions().getByType(TestRetryTaskExtension.class);
testRetry.getFailOnPassedAfterRetry().set(true);
testRetry.getMaxRetries().set(isCi() ? 3 : 0);
+1 -1
View File
@@ -17,4 +17,4 @@ changelog:
- "type: dependency-upgrade"
contributors:
exclude:
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll"]
names: ["bclozel", "jhoeller", "poutsma", "rstoyanchev", "sbrannen", "sdeleuze", "snicoll", "simonbasle"]
+2 -3
View File
@@ -1,4 +1,4 @@
FROM ubuntu:focal-20220922
FROM ubuntu:jammy-20230425
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
@@ -6,7 +6,6 @@ RUN ./setup.sh
ENV JAVA_HOME /opt/openjdk/java17
ENV JDK17 /opt/openjdk/java17
ENV JDK18 /opt/openjdk/java18
ENV JDK19 /opt/openjdk/java19
ENV JDK20 /opt/openjdk/java20
ENV PATH $JAVA_HOME/bin:$PATH
+3 -6
View File
@@ -3,13 +3,10 @@ set -e
case "$1" in
java17)
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.5+8/bellsoft-jdk17.0.5+8-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.7+7/bellsoft-jdk17.0.7+7-linux-amd64.tar.gz"
;;
java18)
echo "https://github.com/bell-sw/Liberica/releases/download/18.0.2.1%2B1/bellsoft-jdk18.0.2.1+1-linux-amd64.tar.gz"
;;
java19)
echo "https://github.com/bell-sw/Liberica/releases/download/19.0.1%2B11/bellsoft-jdk19.0.1+11-linux-amd64.tar.gz"
java20)
echo "https://github.com/bell-sw/Liberica/releases/download/20.0.1+10/bellsoft-jdk20.0.1+10-linux-amd64.tar.gz"
;;
*)
echo $"Unknown java version"
+1 -1
View File
@@ -20,7 +20,7 @@ curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/c
mkdir -p /opt/openjdk
pushd /opt/openjdk > /dev/null
for jdk in java17 java18 java19
for jdk in java17 java20
do
JDK_URL=$( /get-jdk-url.sh $jdk )
mkdir $jdk
+1 -4
View File
@@ -3,12 +3,9 @@ github-repo-name: "spring-projects/spring-framework"
sonatype-staging-profile: "org.springframework"
docker-hub-organization: "springci"
artifactory-server: "https://repo.spring.io"
branch: "main"
branch: "6.0.x"
milestone: "6.0.x"
build-name: "spring-framework"
pipeline-name: "spring-framework"
concourse-url: "https://ci.spring.io"
registry-mirror-host: docker.repo.spring.io
registry-mirror-username: ((artifactory-username))
registry-mirror-password: ((artifactory-password))
task-timeout: 1h00m
+20 -97
View File
@@ -23,11 +23,6 @@ anchors:
docker-resource-source: &docker-resource-source
username: ((docker-hub-username))
password: ((docker-hub-password))
tag: ((milestone))
registry-mirror-vars: &registry-mirror-vars
registry-mirror-host: ((registry-mirror-host))
registry-mirror-username: ((registry-mirror-username))
registry-mirror-password: ((registry-mirror-password))
slack-fail-params: &slack-fail-params
text: >
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
@@ -48,31 +43,31 @@ resource_types:
- name: registry-image
type: registry-image
source:
<<: *docker-resource-source
repository: concourse/registry-image-resource
tag: 1.5.0
- name: artifactory-resource
type: registry-image
source:
<<: *docker-resource-source
repository: springio/artifactory-resource
tag: 0.0.17
tag: 0.0.18
- name: github-release
type: registry-image
source:
<<: *docker-resource-source
repository: concourse/github-release-resource
tag: 1.5.5
- name: github-status-resource
type: registry-image
source:
<<: *docker-resource-source
repository: dpb587/github-status-resource
tag: master
- name: pull-request
type: registry-image
source:
repository: teliaoss/github-pr-resource
tag: v0.23.0
- name: slack-notification
type: registry-image
source:
<<: *docker-resource-source
repository: cfcommunity/slack-notification-resource
tag: latest
resources:
@@ -94,6 +89,7 @@ resources:
source:
<<: *docker-resource-source
repository: ((docker-hub-organization))/spring-framework-ci
tag: ((milestone))
- name: every-morning
type: time
icon: alarm
@@ -109,14 +105,6 @@ resources:
username: ((artifactory-username))
password: ((artifactory-password))
build_name: ((build-name))
- name: git-pull-request
type: pull-request
icon: source-pull
source:
access_token: ((github-ci-pull-request-token))
repository: ((github-repo-name))
base_branch: ((branch))
ignore_paths: ["ci/*"]
- name: repo-status-build
type: github-status-resource
icon: eye-check-outline
@@ -125,22 +113,14 @@ resources:
access_token: ((github-ci-status-token))
branch: ((branch))
context: build
- name: repo-status-jdk18-build
- name: repo-status-jdk20-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk18-build
- name: repo-status-jdk19-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk19-build
context: jdk20-build
- name: slack-alert
type: slack-notification
icon: slack
@@ -176,7 +156,7 @@ jobs:
image: ci-image
vars:
ci-image-name: ci-image
<<: *registry-mirror-vars
<<: *docker-resource-source
- put: ci-image
params:
image: ci-image/image.tar
@@ -237,7 +217,7 @@ jobs:
"zip.type": "schema"
get_params:
threads: 8
- name: jdk18-build
- name: jdk20-build
serial: true
public: true
plan:
@@ -245,7 +225,7 @@ jobs:
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk18-build
- put: repo-status-jdk20-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
@@ -254,76 +234,17 @@ jobs:
privileged: true
timeout: ((task-timeout))
params:
TEST_TOOLCHAIN: 18
TEST_TOOLCHAIN: 20
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk18-build
- put: repo-status-jdk20-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk18-build
- put: repo-status-jdk20-build
params: { state: "success", commit: "git-repo" }
- name: jdk19-build
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk19-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
image: ci-image
file: git-repo/ci/tasks/check-project.yml
privileged: true
timeout: ((task-timeout))
params:
TEST_TOOLCHAIN: 19
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk19-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk19-build
params: { state: "success", commit: "git-repo" }
- name: build-pull-requests
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
resource: git-pull-request
trigger: true
version: every
- do:
- put: git-pull-request
params:
path: git-repo
status: pending
- task: build-pr
image: ci-image
file: git-repo/ci/tasks/build-pr.yml
privileged: true
timeout: ((task-timeout))
params:
BRANCH: ((branch))
on_success:
put: git-pull-request
params:
path: git-repo
status: success
on_failure:
put: git-pull-request
params:
path: git-repo
status: failure
- name: stage-milestone
serial: true
plan:
@@ -365,6 +286,7 @@ jobs:
params:
RELEASE_TYPE: M
<<: *github-task-params
<<: *docker-resource-source
- put: github-pre-release
params:
<<: *changelog-task-params
@@ -403,6 +325,7 @@ jobs:
file: git-repo/ci/tasks/promote-version.yml
params:
RELEASE_TYPE: RC
<<: *docker-resource-source
<<: *artifactory-task-params
- task: generate-changelog
file: git-repo/ci/tasks/generate-changelog.yml
@@ -447,6 +370,7 @@ jobs:
file: git-repo/ci/tasks/promote-version.yml
params:
RELEASE_TYPE: RELEASE
<<: *docker-resource-source
<<: *artifactory-task-params
<<: *sonatype-task-params
- name: create-github-release
@@ -464,6 +388,7 @@ jobs:
file: git-repo/ci/tasks/generate-changelog.yml
params:
RELEASE_TYPE: RELEASE
<<: *docker-resource-source
<<: *github-task-params
- put: github-release
params:
@@ -471,10 +396,8 @@ jobs:
groups:
- name: "builds"
jobs: ["build", "jdk18-build", "jdk19-build"]
jobs: ["build", "jdk20-build"]
- name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
jobs: ["build-ci-images"]
- name: "pull-requests"
jobs: [ "build-pull-requests" ]
+2 -2
View File
@@ -4,6 +4,6 @@ set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK18 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK20 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check antora
popd > /dev/null
+2 -4
View File
@@ -5,10 +5,8 @@ image_resource:
source:
repository: concourse/oci-build-task
tag: 0.10.0
registry_mirror:
host: ((registry-mirror-host))
username: ((registry-mirror-username))
password: ((registry-mirror-password))
username: ((docker-hub-username))
password: ((docker-hub-password))
inputs:
- name: ci-images-git-repo
outputs:
+2
View File
@@ -5,6 +5,8 @@ image_resource:
source:
repository: springio/github-changelog-generator
tag: '0.0.8'
username: ((docker-hub-username))
password: ((docker-hub-password))
inputs:
- name: git-repo
- name: artifactory-repo
+2
View File
@@ -5,6 +5,8 @@ image_resource:
source:
repository: springio/concourse-release-scripts
tag: '0.3.4'
username: ((docker-hub-username))
password: ((docker-hub-password))
inputs:
- name: git-repo
- name: artifactory-repo
+32
View File
@@ -0,0 +1,32 @@
name: framework
version: true
title: Spring Framework
nav:
- modules/ROOT/nav.adoc
ext:
collector:
run:
command: gradlew -q -PbuildSrc.skipTests=true "-Dorg.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError" :framework-docs:generateAntoraResources
local: true
scan:
dir: ./build/generated-antora-resources
asciidoc:
attributes:
attribute-missing: 'warn'
# FIXME: the copyright is not removed
# FIXME: The package is not renamed
chomp: 'all'
include-java: 'example$docs-src/main/java/org/springframework/docs'
spring-framework-main-code: 'https://github.com/spring-projects/spring-framework/tree/main'
docs-site: 'https://docs.spring.io'
docs-spring: "{docs-site}/spring-framework/docs/{spring-version}"
docs-spring-framework: '{docs-site}/spring-framework/docs/{spring-version}'
api-spring-framework: '{docs-spring-framework}/javadoc-api/org/springframework'
docs-graalvm: 'https://www.graalvm.org/22.3/reference-manual'
docs-spring-boot: '{docs-site}/spring-boot/docs/current/reference'
docs-spring-gemfire: '{docs-site}/spring-gemfire/docs/current/reference'
docs-spring-security: '{docs-site}/spring-security/reference'
gh-rsocket: 'https://github.com/rsocket'
gh-rsocket-extensions: '{gh-rsocket}/rsocket/blob/master/Extensions'
gh-rsocket-java: '{gh-rsocket}/rsocket-java{gh-rsocket}/rsocket-java'
+53 -80
View File
@@ -1,27 +1,56 @@
plugins {
id 'kotlin'
id 'io.spring.antora.generate-antora-yml' version '0.0.1'
id 'org.antora' version '1.0.0'
}
description = "Spring Framework Docs"
apply plugin: 'kotlin'
apply plugin: 'org.asciidoctor.jvm.convert'
apply plugin: 'org.asciidoctor.jvm.pdf'
apply from: "${rootDir}/gradle/publications.gradle"
configurations {
asciidoctorExtensions
antora {
version = '3.2.0-alpha.2'
playbook = 'cached-antora-playbook.yml'
playbookProvider {
repository = 'spring-projects/spring-framework'
branch = 'docs-build'
path = 'lib/antora/templates/per-branch-antora-playbook.yml'
checkLocalBranch = true
}
options = ['--clean', '--stacktrace']
environment = [
'ALGOLIA_API_KEY': '82c7ead946afbac3cf98c32446154691',
'ALGOLIA_APP_ID': '244V8V9FGG',
'ALGOLIA_INDEX_NAME': 'framework-docs'
]
dependencies = [
'@antora/atlas-extension': '1.0.0-alpha.1',
'@antora/collector-extension': '1.0.0-alpha.3',
'@asciidoctor/tabs': '1.0.0-beta.3',
'@opendevise/antora-release-line-extension': '1.0.0-alpha.2',
'@springio/antora-extensions': '1.3.0',
'@springio/asciidoctor-extensions': '1.0.0-alpha.9'
]
}
dependencies {
api(project(":spring-context"))
api(project(":spring-web"))
implementation(project(":spring-core-test"))
implementation("org.assertj:assertj-core")
tasks.named("generateAntoraYml") {
asciidocAttributes = project.provider( {
return ["spring-version": project.version ]
} )
}
checkstyle {
sourceSets = []
tasks.create("generateAntoraResources") {
dependsOn 'generateAntoraYml'
}
// Commented out for now:
// https://github.com/spring-projects/spring-framework/issues/30481
// tasks.named("check") {
// dependsOn 'antora'
// }
jar {
enabled = false
}
@@ -30,16 +59,21 @@ javadoc {
enabled = false
}
dependencies {
asciidoctorExtensions "io.spring.asciidoctor.backends:spring-asciidoctor-backends:0.0.3"
}
repositories {
maven {
url "https://repo.spring.io/release"
}
}
dependencies {
api(project(":spring-context"))
api(project(":spring-web"))
api("jakarta.servlet:jakarta.servlet-api")
implementation(project(":spring-core-test"))
implementation("org.assertj:assertj-core")
}
/**
* Produce Javadoc for all Spring Framework modules in "build/docs/javadoc"
*/
@@ -70,7 +104,7 @@ task api(type: Javadoc) {
overview = "framework-docs/src/docs/api/overview.html"
splitIndex = true
links(project.ext.javadocLinks)
addBooleanOption('Xdoclint:syntax', true) // only check syntax with doclint
addBooleanOption('Xdoclint:syntax,reference', true) // only check syntax and reference with doclint
addBooleanOption('Werror', true) // fail build on Javadoc warnings
}
source moduleProjects.collect { project ->
@@ -91,64 +125,10 @@ rootProject.tasks.dokkaHtmlMultiModule.configure {
outputDirectory.set(project.file("$buildDir/docs/kdoc"))
}
asciidoctorj {
version = '2.4.3'
fatalWarnings ".*"
options doctype: 'book', eruby: 'erubis'
attributes([
icons: 'font',
idprefix: '',
idseparator: '-',
revnumber: project.version,
sectanchors: '',
sectnums: '',
'spring-version': project.version
])
}
/**
* Generate the Spring Framework Reference documentation from
* "src/docs/asciidoc" in "build/docs/ref-docs/html5".
* Zip all Java docs (javadoc & kdoc) into a single archive
*/
asciidoctor {
baseDirFollowsSourceDir()
configurations "asciidoctorExtensions"
sources {
include '*.adoc'
}
outputDir "$buildDir/docs/ref-docs/html5"
outputOptions {
backends "spring-html"
}
logDocuments = true
resources {
from(sourceDir) {
include 'images/*.png'
}
}
}
/**
* Generate the Spring Framework Reference documentation from "src/docs/asciidoc"
* in "build/docs/ref-docs/pdf".
*/
asciidoctorPdf {
baseDirFollowsSourceDir()
configurations 'asciidoctorExtensions'
sources {
include 'spring-framework.adocbook'
}
outputDir "$buildDir/docs/ref-docs/pdf"
forkOptions {
jvmArgs += ["--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED"]
}
logDocuments = true
}
/**
* Zip all docs (API and reference) into a single archive
*/
task docsZip(type: Zip, dependsOn: ['api', 'asciidoctor', 'asciidoctorPdf', rootProject.tasks.dokkaHtmlMultiModule]) {
task docsZip(type: Zip, dependsOn: ['api', rootProject.tasks.dokkaHtmlMultiModule]) {
group = "Distribution"
description = "Builds -${archiveClassifier} archive containing api and reference " +
"for deployment at https://docs.spring.io/spring-framework/docs/."
@@ -161,12 +141,6 @@ task docsZip(type: Zip, dependsOn: ['api', 'asciidoctor', 'asciidoctorPdf', root
from (api) {
into "javadoc-api"
}
from ("$asciidoctor.outputDir") {
into "reference/html"
}
from ("$asciidoctorPdf.outputDir") {
into "reference/pdf"
}
from (rootProject.tasks.dokkaHtmlMultiModule.outputDirectory) {
into "kdoc-api"
}
@@ -247,7 +221,6 @@ task distZip(type: Zip, dependsOn: [docsZip, schemaZip]) {
distZip.mustRunAfter moduleProjects.check
publishing {
publications {
mavenJava(MavenPublication) {
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

@@ -0,0 +1,612 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generated by Microsoft Visio 11.0, SVG Export, v1.0 spring-overview.svg Page-1 -->
<svg
xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="6.4728541in"
height="5.9522467in"
viewBox="0 0 466.04561 428.56238"
xml:space="preserve"
class="st5"
id="svg5499"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="mvc-splitted-contexts.svg"
style="font-size:12px;overflow:visible;color-interpolation-filters:sRGB;fill:none;fill-rule:evenodd;stroke-linecap:square;stroke-miterlimit:3"
inkscape:export-filename="/Users/seb/Workspace/spring-framework/src/asciidoc/images/mvc-splitted-contexts.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"><metadata
id="metadata5713"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="https://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs5711"><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker9162"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path9164" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker8834"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path8836" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker8518"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path8520" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker8214"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path8216" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker7922"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path7924" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker7641"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path7644" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker7373"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path7375" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker7117"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path7119" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker6873"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path6875" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker6641"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path6643" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker6421"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path6423" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker6213"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path6215" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker6017"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path6019" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker5833"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path5835" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker5661"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path5663" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker5501"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path5503" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker5353"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path5355" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker5217"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path5219" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker5093"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path5095" /></marker><marker
inkscape:isstock="true"
style="overflow:visible;"
id="marker4981"
refX="0.0"
refY="0.0"
orient="auto"
inkscape:stockid="Arrow2Mend"><path
transform="scale(0.6) rotate(180) translate(0,0)"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
id="path4983" /></marker><marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Mend"
style="overflow:visible;"
inkscape:isstock="true"
inkscape:collect="always"><path
id="path7394"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" /></marker><marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="marker8121"
style="overflow:visible;"
inkscape:isstock="true"><path
id="path8123"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" /></marker><marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="marker8033"
style="overflow:visible;"
inkscape:isstock="true"><path
id="path8035"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="marker7957"
style="overflow:visible;"
inkscape:isstock="true"><path
id="path7959"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Mend"
style="overflow:visible;"
inkscape:isstock="true"><path
id="path7376"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.4) rotate(180) translate(10,0)" /></marker><marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Lend"
style="overflow:visible;"
inkscape:isstock="true"><path
id="path7388"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round;stroke:#000000;stroke-opacity:1"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" /></marker><marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Lend"
style="overflow:visible;"
inkscape:isstock="true"><path
id="path7370"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
transform="scale(0.8) rotate(180) translate(12.5,0)" /></marker></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="1005"
id="namedview5709"
showgrid="false"
inkscape:zoom="1.1640492"
inkscape:cx="134.86698"
inkscape:cy="203.59898"
inkscape:window-x="84"
inkscape:window-y="176"
inkscape:window-maximized="0"
inkscape:current-layer="g5503"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" /><v:documentProperties
v:langID="1033"
v:viewMarkup="false" /><style
type="text/css"
id="style5501"><![CDATA[
.st1 {fill:#969696;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st2 {fill:#dde2cd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st3 {fill:#000000;font-family:Arial;font-size:2.50001em;font-weight:bold}
.st4 {font-size:0.333333em;font-weight:normal}
.st5 {fill:none;fill-rule:evenodd;font-size:12;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
]]></style><g
v:mID="0"
v:index="1"
v:groupContext="foregroundPage"
id="g5503"
transform="matrix(0.99998201,0,0,1.0824094,-40.812382,-98.908648)"><rect
style="fill:#dde2cd;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6599"
width="382.68423"
height="146.09897"
x="87.884865"
y="148.26482" /><v:userDefs><v:ud
v:nameU="SchemeName"
v:val="VT4(Default)" /></v:userDefs><rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect5725"
width="464.31128"
height="374.11411"
x="41.684383"
y="112.3262" /><title
id="title5505">Page-1</title><v:pageProperties
v:drawingScale="0.0393701"
v:pageScale="0.0393701"
v:drawingUnits="24"
v:shadowOffsetX="8.50394"
v:shadowOffsetY="-8.50394" /><v:layer
v:name="Connector"
v:index="0" /><rect
style="fill:#dde2cd;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.53790233;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect5715"
width="322.8194"
height="43.63184"
x="119.95335"
y="-135.66222"
transform="scale(1,-1)" /><text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:30.7580471px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="168.843"
y="124.32391"
id="text5717"
sodipodi:linespacing="125%"
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan5719"
x="168.843"
y="124.32391"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif">DispatcherServlet</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="89.770851"
y="181.20923"
id="text6589"
sodipodi:linespacing="125%"
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan6591"
x="89.770851"
y="181.20923"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif">Servlet WebApplicationContext</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="260.00443"
y="198.41273"
id="text6593"
sodipodi:linespacing="125%"
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan6595"
x="260.00443"
y="198.41273"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle">(containing controllers, view resolvers,</tspan><tspan
sodipodi:role="line"
x="260.00443"
y="212.83057"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
id="tspan6597">and other web-related beans)</tspan></text>
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6620"
width="82.040657"
height="36.72575"
x="114.52653"
y="-259.43161"
transform="scale(1,-1)" /><rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.89166164;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6622"
width="87.843979"
height="36.697304"
x="223.39864"
y="-287.19809"
transform="scale(1,-1)" /><rect
transform="scale(1,-1)"
y="-264.81918"
x="117.92834"
height="36.72575"
width="82.040657"
id="rect6614"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="121.24728"
y="260.14957"
id="text6616"
sodipodi:linespacing="125%"
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan6618"
x="121.24728"
y="260.14957"
style="font-size:11.53426838px">Controllers</tspan></text>
<text
transform="scale(1.0403984,0.96117025)"
sodipodi:linespacing="125%"
id="text6624"
y="282.70709"
x="219.61203"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-size:11.53426838px"
y="282.70709"
x="219.61203"
id="tspan6626"
sodipodi:role="line">ViewResolver</tspan></text>
<rect
transform="scale(1,-1)"
y="-259.30249"
x="332.5405"
height="36.577778"
width="114.4539"
id="rect6628"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.0161339;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="327.51276"
y="255.28464"
id="text6630"
sodipodi:linespacing="125%"
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan6632"
x="327.51276"
y="255.28464"
style="font-size:11.53426838px">HandlerMapping</tspan></text>
<rect
y="338.69724"
x="87.803261"
height="121.5683"
width="382.84744"
id="rect6634"
style="fill:#dde2cd;fill-opacity:1;stroke:#000000;stroke-width:1.53790233;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
transform="scale(1.0403984,0.96117025)"
sodipodi:linespacing="125%"
id="text6636"
y="376.61673"
x="108.61351"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:23.06853676px;font-family:sans-serif;-inkscape-font-specification:sans-serif"
y="376.61673"
x="108.61351"
id="tspan6638"
sodipodi:role="line">Root WebApplicationContext</tspan></text>
<text
transform="scale(1.0403984,0.96117025)"
sodipodi:linespacing="125%"
id="text6640"
y="395.35812"
x="260.93863"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
id="tspan6644"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
y="395.35812"
x="260.93863"
sodipodi:role="line">(containing middle-tier services, datasources, etc.)</tspan></text>
<rect
transform="scale(1,-1)"
y="-440.06805"
x="164.32933"
height="36.72575"
width="82.040657"
id="rect6648"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.86203903;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6650"
width="82.040657"
height="36.72575"
x="167.73116"
y="-445.45563"
transform="scale(1,-1)" /><text
transform="scale(1.0403984,0.96117025)"
sodipodi:linespacing="125%"
id="text6652"
y="448.55054"
x="175.87148"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-size:11.53426838px"
y="448.55054"
x="175.87148"
id="tspan6654"
sodipodi:role="line">Services</tspan></text>
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88435173;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1"
id="rect6656"
width="86.393044"
height="36.704323"
x="306.86328"
y="-439.60837"
transform="scale(1,-1)" /><rect
transform="scale(1,-1)"
y="-444.99475"
x="310.26624"
height="36.701977"
width="86.876686"
id="rect6658"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.88679528;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1" /><text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="305.30771"
y="448.55054"
id="text6660"
sodipodi:linespacing="125%"
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
id="tspan6662"
x="305.30771"
y="448.55054"
style="font-size:11.53426838px">Repositories</tspan></text>
<path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.76895118px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 265.33234,295.60379 c 0,42.65169 0,42.65169 0,0 z"
id="path7643"
inkscape:connector-curvature="0" /><path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.46028023;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
d="m 270.43505,294.70585 c 0,39.4721 0,39.87903 0,39.87903"
id="path7645"
inkscape:connector-curvature="0" /><text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30.7580471px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="352.55331"
y="333.03622"
id="text4963"
sodipodi:linespacing="125%"
transform="scale(1.0403984,0.96117025)"><tspan
sodipodi:role="line"
x="352.55331"
y="333.03622"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.53426838px;font-family:sans-serif;-inkscape-font-specification:sans-serif;text-align:center;text-anchor:middle"
id="tspan4965">Delegates if no bean found</tspan></text>
</g></svg>

After

Width:  |  Height:  |  Size: 30 KiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

+1
View File
@@ -0,0 +1 @@
../../../src
+434
View File
@@ -0,0 +1,434 @@
* xref:overview.adoc[Overview]
* xref:core.adoc[]
** xref:core/beans.adoc[]
*** xref:core/beans/introduction.adoc[]
*** xref:core/beans/basics.adoc[]
*** xref:core/beans/definition.adoc[]
*** xref:core/beans/dependencies.adoc[]
**** xref:core/beans/dependencies/factory-collaborators.adoc[]
**** xref:core/beans/dependencies/factory-properties-detailed.adoc[]
**** xref:core/beans/dependencies/factory-dependson.adoc[]
**** xref:core/beans/dependencies/factory-lazy-init.adoc[]
**** xref:core/beans/dependencies/factory-autowire.adoc[]
**** xref:core/beans/dependencies/factory-method-injection.adoc[]
*** xref:core/beans/factory-scopes.adoc[]
*** xref:core/beans/factory-nature.adoc[]
*** xref:core/beans/child-bean-definitions.adoc[]
*** xref:core/beans/factory-extension.adoc[]
*** xref:core/beans/annotation-config.adoc[]
**** xref:core/beans/annotation-config/autowired.adoc[]
**** xref:core/beans/annotation-config/autowired-primary.adoc[]
**** xref:core/beans/annotation-config/autowired-qualifiers.adoc[]
**** xref:core/beans/annotation-config/generics-as-qualifiers.adoc[]
**** xref:core/beans/annotation-config/custom-autowire-configurer.adoc[]
**** xref:core/beans/annotation-config/resource.adoc[]
**** xref:core/beans/annotation-config/value-annotations.adoc[]
**** xref:core/beans/annotation-config/postconstruct-and-predestroy-annotations.adoc[]
*** xref:core/beans/classpath-scanning.adoc[]
*** xref:core/beans/standard-annotations.adoc[]
*** xref:core/beans/java.adoc[]
**** xref:core/beans/java/basic-concepts.adoc[]
**** xref:core/beans/java/instantiating-container.adoc[]
**** xref:core/beans/java/bean-annotation.adoc[]
**** xref:core/beans/java/configuration-annotation.adoc[]
**** xref:core/beans/java/composing-configuration-classes.adoc[]
*** xref:core/beans/environment.adoc[]
*** xref:core/beans/context-load-time-weaver.adoc[]
*** xref:core/beans/context-introduction.adoc[]
*** xref:core/beans/beanfactory.adoc[]
** xref:core/resources.adoc[]
** xref:core/validation.adoc[]
*** xref:core/validation/validator.adoc[]
*** xref:core/validation/conversion.adoc[]
*** xref:core/validation/beans-beans.adoc[]
*** xref:core/validation/convert.adoc[]
*** xref:core/validation/format.adoc[]
*** xref:core/validation/format-configuring-formatting-globaldatetimeformat.adoc[]
*** xref:core/validation/beanvalidation.adoc[]
** xref:core/expressions.adoc[]
*** xref:core/expressions/evaluation.adoc[]
*** xref:core/expressions/beandef.adoc[]
*** xref:core/expressions/language-ref.adoc[]
**** xref:core/expressions/language-ref/literal.adoc[]
**** xref:core/expressions/language-ref/properties-arrays.adoc[]
**** xref:core/expressions/language-ref/inline-lists.adoc[]
**** xref:core/expressions/language-ref/inline-maps.adoc[]
**** xref:core/expressions/language-ref/array-construction.adoc[]
**** xref:core/expressions/language-ref/methods.adoc[]
**** xref:core/expressions/language-ref/operators.adoc[]
**** xref:core/expressions/language-ref/types.adoc[]
**** 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/bean-references.adoc[]
**** xref:core/expressions/language-ref/operator-ternary.adoc[]
**** xref:core/expressions/language-ref/operator-elvis.adoc[]
**** xref:core/expressions/language-ref/operator-safe-navigation.adoc[]
**** xref:core/expressions/language-ref/collection-selection.adoc[]
**** xref:core/expressions/language-ref/collection-projection.adoc[]
**** xref:core/expressions/language-ref/templating.adoc[]
*** xref:core/expressions/example-classes.adoc[]
** xref:core/aop.adoc[]
*** xref:core/aop/introduction-defn.adoc[]
*** xref:core/aop/introduction-spring-defn.adoc[]
*** xref:core/aop/introduction-proxies.adoc[]
*** xref:core/aop/ataspectj.adoc[]
**** xref:core/aop/ataspectj/aspectj-support.adoc[]
**** xref:core/aop/ataspectj/at-aspectj.adoc[]
**** xref:core/aop/ataspectj/pointcuts.adoc[]
**** xref:core/aop/ataspectj/advice.adoc[]
**** xref:core/aop/ataspectj/introductions.adoc[]
**** xref:core/aop/ataspectj/instantiation-models.adoc[]
**** xref:core/aop/ataspectj/example.adoc[]
*** xref:core/aop/schema.adoc[]
*** xref:core/aop/choosing.adoc[]
*** xref:core/aop/mixing-styles.adoc[]
*** xref:core/aop/proxying.adoc[]
*** xref:core/aop/aspectj-programmatic.adoc[]
*** xref:core/aop/using-aspectj.adoc[]
*** xref:core/aop/resources.adoc[]
** xref:core/aop-api.adoc[]
*** xref:core/aop-api/pointcuts.adoc[]
*** xref:core/aop-api/advice.adoc[]
*** xref:core/aop-api/advisor.adoc[]
*** xref:core/aop-api/pfb.adoc[]
*** xref:core/aop-api/concise-proxy.adoc[]
*** xref:core/aop-api/prog.adoc[]
*** xref:core/aop-api/advised.adoc[]
*** xref:core/aop-api/autoproxy.adoc[]
*** xref:core/aop-api/targetsource.adoc[]
*** xref:core/aop-api/extensibility.adoc[]
** xref:core/null-safety.adoc[]
** xref:core/databuffer-codec.adoc[]
** xref:core/spring-jcl.adoc[]
** xref:core/aot.adoc[]
** xref:core/appendix.adoc[]
*** 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/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/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-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-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[]
*** xref:data-access/transaction/strategies.adoc[]
*** xref:data-access/transaction/tx-resource-synchronization.adoc[]
*** xref:data-access/transaction/declarative.adoc[]
**** xref:data-access/transaction/declarative/tx-decl-explained.adoc[]
**** xref:data-access/transaction/declarative/first-example.adoc[]
**** xref:data-access/transaction/declarative/rolling-back.adoc[]
**** xref:data-access/transaction/declarative/diff-tx.adoc[]
**** xref:data-access/transaction/declarative/txadvice-settings.adoc[]
**** xref:data-access/transaction/declarative/annotations.adoc[]
**** xref:data-access/transaction/declarative/tx-propagation.adoc[]
**** xref:data-access/transaction/declarative/applying-more-than-just-tx-advice.adoc[]
**** xref:data-access/transaction/declarative/aspectj.adoc[]
*** xref:data-access/transaction/programmatic.adoc[]
*** xref:data-access/transaction/tx-decl-vs-prog.adoc[]
*** xref:data-access/transaction/event.adoc[]
*** xref:data-access/transaction/application-server-integration.adoc[]
*** xref:data-access/transaction/solutions-to-common-problems.adoc[]
*** xref:data-access/transaction/resources.adoc[]
** xref:data-access/dao.adoc[]
** xref:data-access/jdbc.adoc[]
*** xref:data-access/jdbc/choose-style.adoc[]
*** xref:data-access/jdbc/packages.adoc[]
*** xref:data-access/jdbc/core.adoc[]
*** xref:data-access/jdbc/connections.adoc[]
*** xref:data-access/jdbc/advanced.adoc[]
*** xref:data-access/jdbc/simple.adoc[]
*** xref:data-access/jdbc/object.adoc[]
*** xref:data-access/jdbc/parameter-handling.adoc[]
*** xref:data-access/jdbc/embedded-database-support.adoc[]
*** xref:data-access/jdbc/initializing-datasource.adoc[]
** xref:data-access/r2dbc.adoc[]
** xref:data-access/orm.adoc[]
*** xref:data-access/orm/introduction.adoc[]
*** xref:data-access/orm/general.adoc[]
*** xref:data-access/orm/hibernate.adoc[]
*** xref:data-access/orm/jpa.adoc[]
** xref:data-access/oxm.adoc[]
** xref:data-access/appendix.adoc[]
* xref:web.adoc[]
** xref:web/webmvc.adoc[]
*** xref:web/webmvc/mvc-servlet.adoc[]
**** xref:web/webmvc/mvc-servlet/context-hierarchy.adoc[]
**** xref:web/webmvc/mvc-servlet/special-bean-types.adoc[]
**** xref:web/webmvc/mvc-servlet/config.adoc[]
**** xref:web/webmvc/mvc-servlet/container-config.adoc[]
**** xref:web/webmvc/mvc-servlet/sequence.adoc[]
**** xref:web/webmvc/mvc-servlet/handlermapping-path.adoc[]
**** xref:web/webmvc/mvc-servlet/handlermapping-interceptor.adoc[]
**** xref:web/webmvc/mvc-servlet/exceptionhandlers.adoc[]
**** xref:web/webmvc/mvc-servlet/viewresolver.adoc[]
**** xref:web/webmvc/mvc-servlet/localeresolver.adoc[]
**** xref:web/webmvc/mvc-servlet/themeresolver.adoc[]
**** xref:web/webmvc/mvc-servlet/multipart.adoc[]
**** xref:web/webmvc/mvc-servlet/logging.adoc[]
*** xref:web/webmvc/filters.adoc[]
*** xref:web/webmvc/mvc-controller.adoc[]
**** xref:web/webmvc/mvc-controller/ann.adoc[]
**** xref:web/webmvc/mvc-controller/ann-requestmapping.adoc[]
**** xref:web/webmvc/mvc-controller/ann-methods.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/arguments.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/return-types.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/typeconversion.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/matrix-variables.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/requestparam.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/requestheader.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/cookievalue.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/sessionattributes.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/sessionattribute.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/requestattrib.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/redirecting-passing-data.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/flash-attributes.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/multipart-forms.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/requestbody.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/httpentity.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/responsebody.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/responseentity.adoc[]
***** xref:web/webmvc/mvc-controller/ann-methods/jackson.adoc[]
**** xref:web/webmvc/mvc-controller/ann-modelattrib-methods.adoc[]
**** xref:web/webmvc/mvc-controller/ann-initbinder.adoc[]
**** xref:web/webmvc/mvc-controller/ann-exceptionhandler.adoc[]
**** xref:web/webmvc/mvc-controller/ann-advice.adoc[]
*** xref:web/webmvc-functional.adoc[]
*** xref:web/webmvc/mvc-uri-building.adoc[]
*** xref:web/webmvc/mvc-ann-async.adoc[]
*** xref:web/webmvc-cors.adoc[]
*** xref:web/webmvc/mvc-ann-rest-exceptions.adoc[]
*** xref:web/webmvc/mvc-security.adoc[]
*** xref:web/webmvc/mvc-caching.adoc[]
*** xref:web/webmvc-view.adoc[]
**** xref:web/webmvc-view/mvc-thymeleaf.adoc[]
**** 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-jsp.adoc[]
**** xref:web/webmvc-view/mvc-feeds.adoc[]
**** xref:web/webmvc-view/mvc-document.adoc[]
**** xref:web/webmvc-view/mvc-jackson.adoc[]
**** xref:web/webmvc-view/mvc-xml-marshalling.adoc[]
**** xref:web/webmvc-view/mvc-xslt.adoc[]
*** xref:web/webmvc/mvc-config.adoc[]
**** xref:web/webmvc/mvc-config/enable.adoc[]
**** xref:web/webmvc/mvc-config/customize.adoc[]
**** xref:web/webmvc/mvc-config/conversion.adoc[]
**** xref:web/webmvc/mvc-config/validation.adoc[]
**** xref:web/webmvc/mvc-config/interceptors.adoc[]
**** xref:web/webmvc/mvc-config/content-negotiation.adoc[]
**** xref:web/webmvc/mvc-config/message-converters.adoc[]
**** xref:web/webmvc/mvc-config/view-controller.adoc[]
**** xref:web/webmvc/mvc-config/view-resolvers.adoc[]
**** xref:web/webmvc/mvc-config/static-resources.adoc[]
**** xref:web/webmvc/mvc-config/default-servlet-handler.adoc[]
**** xref:web/webmvc/mvc-config/path-matching.adoc[]
**** xref:web/webmvc/mvc-config/advanced-java.adoc[]
**** xref:web/webmvc/mvc-config/advanced-xml.adoc[]
*** xref:web/webmvc/mvc-http2.adoc[]
** xref:web/webmvc-client.adoc[]
** xref:web/webmvc-test.adoc[]
** xref:web/websocket.adoc[]
*** xref:web/websocket/server.adoc[]
*** xref:web/websocket/fallback.adoc[]
*** xref:web/websocket/stomp.adoc[]
**** xref:web/websocket/stomp/overview.adoc[]
**** xref:web/websocket/stomp/benefits.adoc[]
**** xref:web/websocket/stomp/enable.adoc[]
**** xref:web/websocket/stomp/server-config.adoc[]
**** xref:web/websocket/stomp/message-flow.adoc[]
**** xref:web/websocket/stomp/handle-annotations.adoc[]
**** xref:web/websocket/stomp/handle-send.adoc[]
**** xref:web/websocket/stomp/handle-simple-broker.adoc[]
**** xref:web/websocket/stomp/handle-broker-relay.adoc[]
**** xref:web/websocket/stomp/handle-broker-relay-configure.adoc[]
**** xref:web/websocket/stomp/destination-separator.adoc[]
**** xref:web/websocket/stomp/authentication.adoc[]
**** xref:web/websocket/stomp/authentication-token-based.adoc[]
**** xref:web/websocket/stomp/authorization.adoc[]
**** xref:web/websocket/stomp/user-destination.adoc[]
**** xref:web/websocket/stomp/ordered-messages.adoc[]
**** xref:web/websocket/stomp/application-context-events.adoc[]
**** xref:web/websocket/stomp/interceptors.adoc[]
**** xref:web/websocket/stomp/client.adoc[]
**** xref:web/websocket/stomp/scope.adoc[]
**** xref:web/websocket/stomp/configuration-performance.adoc[]
**** xref:web/websocket/stomp/stats.adoc[]
**** xref:web/websocket/stomp/testing.adoc[]
** xref:web/integration.adoc[]
* xref:web-reactive.adoc[]
** xref:web/webflux.adoc[]
*** xref:web/webflux/new-framework.adoc[]
*** xref:web/webflux/reactive-spring.adoc[]
*** xref:web/webflux/dispatcher-handler.adoc[]
*** xref:web/webflux/controller.adoc[]
**** xref:web/webflux/controller/ann.adoc[]
**** xref:web/webflux/controller/ann-requestmapping.adoc[]
**** xref:web/webflux/controller/ann-methods.adoc[]
***** xref:web/webflux/controller/ann-methods/arguments.adoc[]
***** xref:web/webflux/controller/ann-methods/return-types.adoc[]
***** xref:web/webflux/controller/ann-methods/typeconversion.adoc[]
***** xref:web/webflux/controller/ann-methods/matrix-variables.adoc[]
***** xref:web/webflux/controller/ann-methods/requestparam.adoc[]
***** xref:web/webflux/controller/ann-methods/requestheader.adoc[]
***** xref:web/webflux/controller/ann-methods/cookievalue.adoc[]
***** xref:web/webflux/controller/ann-methods/modelattrib-method-args.adoc[]
***** xref:web/webflux/controller/ann-methods/sessionattributes.adoc[]
***** xref:web/webflux/controller/ann-methods/sessionattribute.adoc[]
***** xref:web/webflux/controller/ann-methods/requestattrib.adoc[]
***** xref:web/webflux/controller/ann-methods/multipart-forms.adoc[]
***** xref:web/webflux/controller/ann-methods/requestbody.adoc[]
***** xref:web/webflux/controller/ann-methods/httpentity.adoc[]
***** xref:web/webflux/controller/ann-methods/responsebody.adoc[]
***** xref:web/webflux/controller/ann-methods/responseentity.adoc[]
***** xref:web/webflux/controller/ann-methods/jackson.adoc[]
**** xref:web/webflux/controller/ann-modelattrib-methods.adoc[]
**** xref:web/webflux/controller/ann-initbinder.adoc[]
**** xref:web/webflux/controller/ann-exceptions.adoc[]
**** xref:web/webflux/controller/ann-advice.adoc[]
*** xref:web/webflux-functional.adoc[]
*** xref:web/webflux/uri-building.adoc[]
*** xref:web/webflux-cors.adoc[]
*** xref:web/webflux/ann-rest-exceptions.adoc[]
*** xref:web/webflux/security.adoc[]
*** xref:web/webflux/caching.adoc[]
*** xref:web/webflux-view.adoc[]
*** xref:web/webflux/config.adoc[]
*** xref:web/webflux/http2.adoc[]
** xref:web/webflux-webclient.adoc[]
*** xref:web/webflux-webclient/client-builder.adoc[]
*** xref:web/webflux-webclient/client-retrieve.adoc[]
*** xref:web/webflux-webclient/client-exchange.adoc[]
*** xref:web/webflux-webclient/client-body.adoc[]
*** xref:web/webflux-webclient/client-filter.adoc[]
*** xref:web/webflux-webclient/client-attributes.adoc[]
*** xref:web/webflux-webclient/client-context.adoc[]
*** xref:web/webflux-webclient/client-synchronous.adoc[]
*** xref:web/webflux-webclient/client-testing.adoc[]
** xref:web/webflux-http-interface-client.adoc[]
** xref:web/webflux-websocket.adoc[]
** xref:web/webflux-test.adoc[]
** xref:rsocket.adoc[]
** xref:web/webflux-reactive-libraries.adoc[]
* xref:integration.adoc[]
** xref:integration/rest-clients.adoc[]
** xref:integration/jms.adoc[]
*** xref:integration/jms/using.adoc[]
*** xref:integration/jms/sending.adoc[]
*** xref:integration/jms/receiving.adoc[]
*** xref:integration/jms/jca-message-endpoint-manager.adoc[]
*** xref:integration/jms/annotated.adoc[]
*** xref:integration/jms/namespace.adoc[]
** xref:integration/jmx.adoc[]
*** xref:integration/jmx/exporting.adoc[]
*** xref:integration/jmx/interface.adoc[]
*** xref:integration/jmx/naming.adoc[]
*** xref:integration/jmx/jsr160.adoc[]
*** xref:integration/jmx/proxy.adoc[]
*** xref:integration/jmx/notifications.adoc[]
*** xref:integration/jmx/resources.adoc[]
** xref:integration/email.adoc[]
** xref:integration/scheduling.adoc[]
** xref:integration/cache.adoc[]
*** xref:integration/cache/strategies.adoc[]
*** xref:integration/cache/annotations.adoc[]
*** xref:integration/cache/jsr-107.adoc[]
*** xref:integration/cache/declarative-xml.adoc[]
*** xref:integration/cache/store-configuration.adoc[]
*** xref:integration/cache/plug.adoc[]
*** xref:integration/cache/specific-config.adoc[]
** xref:integration/observability.adoc[]
** xref:integration/appendix.adoc[]
* xref:languages.adoc[]
** xref:languages/kotlin.adoc[]
*** xref:languages/kotlin/requirements.adoc[]
*** xref:languages/kotlin/extensions.adoc[]
*** xref:languages/kotlin/null-safety.adoc[]
*** xref:languages/kotlin/classes-interfaces.adoc[]
*** xref:languages/kotlin/annotations.adoc[]
*** xref:languages/kotlin/bean-definition-dsl.adoc[]
*** xref:languages/kotlin/web.adoc[]
*** xref:languages/kotlin/coroutines.adoc[]
*** xref:languages/kotlin/spring-projects-in.adoc[]
*** xref:languages/kotlin/getting-started.adoc[]
*** xref:languages/kotlin/resources.adoc[]
** xref:languages/groovy.adoc[]
** xref:languages/dynamic.adoc[]
* xref:appendix.adoc[]
* https://github.com/spring-projects/spring-framework/wiki[Wiki]
@@ -1,7 +1,5 @@
[[appendix]]
= Appendix
include::attributes.adoc[]
include::page-layout.adoc[]
This part of the reference documentation covers topics that apply to multiple modules
within the core Spring Framework.
@@ -27,12 +25,12 @@ The following table lists all currently supported Spring properties.
| `spring.beaninfo.ignore`
| Instructs Spring to use the `Introspector.IGNORE_ALL_BEANINFO` mode when calling the
JavaBeans `Introspector`. See
{api-spring-framework}++/beans/CachedIntrospectionResults.html#IGNORE_BEANINFO_PROPERTY_NAME++[`CachedIntrospectionResults`]
{api-spring-framework}++/beans/StandardBeanInfoFactory.html#IGNORE_BEANINFO_PROPERTY_NAME++[`CachedIntrospectionResults`]
for details.
| `spring.expression.compiler.mode`
| The mode to use when compiling expressions for the
<<core.adoc#expressions-compiler-configuration, Spring Expression Language>>.
xref:core/expressions/evaluation.adoc#expressions-compiler-configuration[Spring Expression Language].
| `spring.getenv.ignore`
| Instructs Spring to ignore operating system environment variables if a Spring
@@ -43,12 +41,12 @@ for details.
| `spring.index.ignore`
| Instructs Spring to ignore the components index located in
`META-INF/spring.components`. See <<core.adoc#beans-scanning-index, Generating an Index
of Candidate Components>>.
`META-INF/spring.components`. See xref:core/beans/classpath-scanning.adoc#beans-scanning-index[Generating an Index of Candidate Components]
.
| `spring.jdbc.getParameterType.ignore`
| Instructs Spring to ignore `java.sql.ParameterMetaData.getParameterType` completely.
See the note in <<data-access.adoc#jdbc-batch-list, Batch Operations with a List of Objects>>.
See the note in xref:data-access/jdbc/advanced.adoc#jdbc-batch-list[Batch Operations with a List of Objects].
| `spring.jndi.ignore`
| Instructs Spring to ignore a default JNDI environment, as an optimization for scenarios
@@ -64,17 +62,17 @@ for details.
| `spring.test.constructor.autowire.mode`
| The default _test constructor autowire mode_ to use if `@TestConstructor` is not present
on a test class. See <<testing.adoc#integration-testing-annotations-testconstructor,
Changing the default test constructor autowire mode>>.
on a test class. See xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-testconstructor[Changing the default test constructor autowire mode]
.
| `spring.test.context.cache.maxSize`
| The maximum size of the context cache in the _Spring TestContext Framework_. See
<<testing.adoc#testcontext-ctx-management-caching, Context Caching>>.
xref:testing/testcontext-framework/ctx-management/caching.adoc[Context Caching].
| `spring.test.enclosing.configuration`
| The default _enclosing configuration inheritance mode_ to use if
`@NestedTestConfiguration` is not present on a test class. See
<<testing.adoc#integration-testing-annotations-nestedtestconfiguration, Changing the
default enclosing configuration inheritance mode>>.
xref:testing/annotations/integration-junit-jupiter.adoc#integration-testing-annotations-nestedtestconfiguration[Changing the default enclosing configuration inheritance mode]
.
|===
@@ -1,5 +1,11 @@
:chomp: default headers packages
// Spring Portfolio
:docs-site: https://docs.spring.io
:docs-spring-boot: {docs-site}/spring-boot/docs/current/reference
:docs-spring-gemfire: {docs-site}/spring-gemfire/docs/current/reference
:docs-spring-security: {docs-site}/spring-security/reference
// spring-asciidoctor-backends Settings
:chomp: default headers packages
:fold: all
// Spring Framework
:docs-spring-framework: {docs-site}/spring-framework/docs/{spring-version}
:api-spring-framework: {docs-spring-framework}/javadoc-api/org/springframework
@@ -7,10 +13,6 @@
:docs-kotlin: {docdir}/../../main/kotlin/org/springframework/docs
:docs-resources: {docdir}/../../main/resources
:spring-framework-main-code: https://github.com/spring-projects/spring-framework/tree/main
// Spring portfolio Links
:docs-spring-boot: {docs-site}/spring-boot/docs/current/reference
:docs-spring-gemfire: {docs-site}/spring-gemfire/docs/current/reference
:docs-spring-security: {docs-site}/spring-security/reference
// Third-party Links
:docs-graalvm: https://www.graalvm.org/22.3/reference-manual
:gh-rsocket: https://github.com/rsocket
@@ -1,7 +1,5 @@
[[spring-core]]
= Core Technologies
include::attributes.adoc[]
include::page-layout.adoc[]
This part of the reference documentation covers all the technologies that are
absolutely integral to the Spring Framework.
@@ -20,24 +18,13 @@ 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.
include::core/core-beans.adoc[leveloffset=+1]
include::core/core-resources.adoc[leveloffset=+1]
include::core/core-validation.adoc[leveloffset=+1]
include::core/core-expressions.adoc[leveloffset=+1]
include::core/core-aop.adoc[leveloffset=+1]
include::core/core-aop-api.adoc[leveloffset=+1]
include::core/core-null-safety.adoc[leveloffset=+1]
include::core/core-databuffer-codec.adoc[leveloffset=+1]
include::core/core-spring-jcl.adoc[leveloffset=+1]
include::core/core-aot.adoc[leveloffset=+1]
include::core/core-appendix.adoc[leveloffset=+1]
@@ -0,0 +1,12 @@
[[aop-api]]
= Spring AOP APIs
:page-section-summary-toc: 1
The previous chapter described the Spring's support for AOP with @AspectJ and schema-based
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.
@@ -0,0 +1,592 @@
[[aop-api-advice]]
= Advice API in Spring
Now we can examine how Spring AOP handles advice.
[[aop-api-advice-lifecycle]]
== Advice Lifecycles
Each advice is a Spring bean. An advice instance can be shared across all advised
objects or be unique to each advised object. This corresponds to per-class or
per-instance advice.
Per-class advice is used most often. It is appropriate for generic advice, such as
transaction advisors. These do not depend on the state of the proxied object or add new
state. They merely act on the method and arguments.
Per-instance advice is appropriate for introductions, to support mixins. In this case,
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.
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"]
----
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
----
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: the return value of the join
point.
The following example shows a simple `MethodInterceptor` implementation:
[tabs]
======
Java::
+
[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 rval = invocation.proceed();
System.out.println("Invocation returned");
return rval;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class DebugInterceptor : MethodInterceptor {
override fun invoke(invocation: MethodInvocation): Any {
println("Before: invocation=[$invocation]")
val rval = invocation.proceed()
println("Invocation returned")
return rval
}
}
----
======
Note the call to the `proceed()` method of `MethodInvocation`. This proceeds down the
interceptor chain towards the join point. Most interceptors invoke this method and
return its return value. However, a `MethodInterceptor`, like any around advice, can
return a different value or throw an exception rather than invoke the proceed method.
However, you do not want to do this without good reason.
NOTE: `MethodInterceptor` implementations offer interoperability with other AOP Alliance-compliant AOP
implementations. The other advice types discussed in the remainder of this section
implement common AOP concepts but in a Spring-specific way. While there is an advantage
in using the most specific advice type, stick with `MethodInterceptor` around advice if
you are likely to want to run the aspect in another AOP framework. Note that pointcuts
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`
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()`
method and, therefore, no possibility of inadvertently failing to proceed down the
interceptor chain.
The following listing shows the `MethodBeforeAdvice` interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
----
(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
propagates back up the interceptor chain. If it is unchecked or on the signature of
the invoked method, it is passed directly to the client. Otherwise, it is
wrapped in an unchecked exception by the AOP proxy.
The following example shows a before advice in Spring, which counts all method invocations:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class CountingBeforeAdvice implements MethodBeforeAdvice {
private int count;
public void before(Method m, Object[] args, Object target) throws Throwable {
++count;
}
public int getCount() {
return count;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class CountingBeforeAdvice : MethodBeforeAdvice {
var count: Int = 0
override fun before(m: Method, args: Array<Any>, target: Any?) {
++count
}
}
----
======
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
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
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"]
----
afterThrowing([Method, args, target], subclassOfThrowable)
----
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 listing show classes that are examples of throws advice.
The following advice is invoked if a `RemoteException` is thrown (including from subclasses):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class RemoteThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException ex) throws Throwable {
// Do something with remote exception
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class RemoteThrowsAdvice : ThrowsAdvice {
fun afterThrowing(ex: RemoteException) {
// Do something with remote exception
}
}
----
======
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",role="primary"]
----
public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
// Do something with all arguments
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class ServletThrowsAdviceWithArguments : ThrowsAdvice {
fun afterThrowing(m: Method, args: Array<Any>, target: Any, ex: ServletException) {
// Do something with all arguments
}
}
----
======
The final example illustrates how these two methods could be used in a single class
that handles both `RemoteException` and `ServletException`. Any number of throws advice
methods can be combined in a single class. The following listing shows the final example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public static class CombinedThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException ex) throws Throwable {
// Do something with remote exception
}
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
// Do something with all arguments
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class CombinedThrowsAdvice : ThrowsAdvice {
fun afterThrowing(ex: RemoteException) {
// Do something with remote exception
}
fun afterThrowing(m: Method, args: Array<Any>, target: Any, ex: ServletException) {
// Do something with all arguments
}
}
----
======
NOTE: If a throws-advice method throws an exception itself, it overrides the
original exception (that is, it changes the exception thrown to the user). The overriding
exception is typically a RuntimeException, which is compatible with any method
signature. However, if a throws-advice method throws a checked exception, it must
match the declared exceptions of the target method and is, hence, to some degree
coupled to specific target method signatures. _Do not throw an undeclared checked
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
`org.springframework.aop.AfterReturningAdvice` interface, which the following listing shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface AfterReturningAdvice extends Advice {
void afterReturning(Object returnValue, Method m, Object[] args, Object target)
throws Throwable;
}
----
An after returning advice has access to the return value (which it cannot modify),
the invoked method, the method's arguments, and the target.
The following after returning advice counts all successful method invocations that have
not thrown exceptions:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class CountingAfterReturningAdvice implements AfterReturningAdvice {
private int count;
public void afterReturning(Object returnValue, Method m, Object[] args, Object target)
throws Throwable {
++count;
}
public int getCount() {
return count;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class CountingAfterReturningAdvice : AfterReturningAdvice {
var count: Int = 0
private set
override fun afterReturning(returnValue: Any?, m: Method, args: Array<Any>, target: Any?) {
++count
}
}
----
======
This advice does not change the execution path. If it throws an exception, it is
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.
Introduction requires an `IntroductionAdvisor` and an `IntroductionInterceptor` that
implement the following interface:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface IntroductionInterceptor extends MethodInterceptor {
boolean implementsInterface(Class intf);
}
----
The `invoke()` method inherited from the AOP Alliance `MethodInterceptor` interface must
implement the introduction. That is, if the invoked method is on an introduced
interface, the introduction interceptor is responsible for handling the method call -- it
cannot invoke `proceed()`.
Introduction advice cannot be used with any pointcut, as it applies only at the class,
rather than the method, level. You can only use introduction advice with the
`IntroductionAdvisor`, which has the following methods:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
ClassFilter getClassFilter();
void validateInterfaces() throws IllegalArgumentException;
}
public interface IntroductionInfo {
Class<?>[] getInterfaces();
}
----
There is no `MethodMatcher` and, hence, no `Pointcut` associated with introduction
advice. Only class filtering is logical.
The `getInterfaces()` method returns the interfaces introduced by this advisor.
The `validateInterfaces()` method is used internally to see whether or not the
introduced interfaces can be implemented by the configured `IntroductionInterceptor`.
Consider an example from the Spring test suite and suppose we want to
introduce the following interface to one or more objects:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public interface Lockable {
void lock();
void unlock();
boolean locked();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
interface Lockable {
fun lock()
fun unlock()
fun locked(): Boolean
}
----
======
This illustrates a mixin. We want to be able to cast advised objects to `Lockable`,
whatever their type and call lock and unlock methods. If we call the `lock()` method, we
want all setter methods to throw a `LockedException`. Thus, we can add an aspect that
provides the ability to make objects immutable without them having any knowledge of it:
a good example of AOP.
First, we need an `IntroductionInterceptor` that does the heavy lifting. In this
case, we extend the `org.springframework.aop.support.DelegatingIntroductionInterceptor`
convenience class. We could implement `IntroductionInterceptor` directly, but using
`DelegatingIntroductionInterceptor` is best for most cases.
The `DelegatingIntroductionInterceptor` is designed to delegate an introduction to an
actual implementation of the introduced interfaces, concealing the use of interception
to do so. You can set the delegate to any object using a constructor argument. The
default delegate (when the no-argument constructor is used) is `this`. Thus, in the next example,
the delegate is the `LockMixin` subclass of `DelegatingIntroductionInterceptor`.
Given a delegate (by default, itself), a `DelegatingIntroductionInterceptor` instance
looks for all interfaces implemented by the delegate (other than
`IntroductionInterceptor`) and supports introductions against any of them.
Subclasses such as `LockMixin` can call the `suppressInterface(Class intf)`
method to suppress interfaces that should not be exposed. However, no matter how many
interfaces an `IntroductionInterceptor` is prepared to support, the
`IntroductionAdvisor` used controls which interfaces are actually exposed. An
introduced interface conceals any implementation of the same interface by the target.
Thus, `LockMixin` extends `DelegatingIntroductionInterceptor` and implements `Lockable`
itself. The superclass automatically picks up that `Lockable` can be supported for
introduction, so we do not need to specify that. We could introduce any number of
interfaces in this way.
Note the use of the `locked` instance variable. This effectively adds additional state
to that held in the target object.
The following example shows the example `LockMixin` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {
private boolean locked;
public void lock() {
this.locked = true;
}
public void unlock() {
this.locked = false;
}
public boolean locked() {
return this.locked;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if (locked() && invocation.getMethod().getName().indexOf("set") == 0) {
throw new LockedException();
}
return super.invoke(invocation);
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class LockMixin : DelegatingIntroductionInterceptor(), Lockable {
private var locked: Boolean = false
fun lock() {
this.locked = true
}
fun unlock() {
this.locked = false
}
fun locked(): Boolean {
return this.locked
}
override fun invoke(invocation: MethodInvocation): Any? {
if (locked() && invocation.method.name.indexOf("set") == 0) {
throw LockedException()
}
return super.invoke(invocation)
}
}
----
======
Often, you need not override the `invoke()` method. The
`DelegatingIntroductionInterceptor` implementation (which calls the `delegate` method if
the method is introduced, otherwise proceeds towards the join point) usually
suffices. In the present case, we need to add a check: no setter method can be invoked
if in locked mode.
The required introduction only needs to hold a distinct
`LockMixin` instance and specify the introduced interfaces (in this case, only
`Lockable`). A more complex example might take a reference to the introduction
interceptor (which would be defined as a prototype). In this case, there is no
configuration relevant for a `LockMixin`, so we create it by using `new`.
The following example shows our `LockMixinAdvisor` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
public LockMixinAdvisor() {
super(new LockMixin(), Lockable.class);
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class LockMixinAdvisor : DefaultIntroductionAdvisor(LockMixin(), Lockable::class.java)
----
======
We can apply this advisor very simply, because it requires no configuration. (However, it
is impossible to use an `IntroductionInterceptor` without an
`IntroductionAdvisor`.) As usual with introductions, the advisor must be per-instance,
as it is stateful. We need a different instance of `LockMixinAdvisor`, and hence
`LockMixin`, for each advised object. The advisor comprises part of the advised object's
state.
We can apply this advisor programmatically by using the `Advised.addAdvisor()` method or
(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.
@@ -0,0 +1,148 @@
[[aop-api-advised]]
= Manipulating Advised Objects
However you create AOP proxies, you can manipulate them BY using the
`org.springframework.aop.framework.Advised` interface. Any AOP proxy can be cast to this
interface, no matter which other interfaces it implements. This interface includes the
following methods:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Advisor[] getAdvisors();
void addAdvice(Advice advice) throws AopConfigException;
void addAdvice(int pos, Advice advice) throws AopConfigException;
void addAdvisor(Advisor advisor) throws AopConfigException;
void addAdvisor(int pos, Advisor advisor) throws AopConfigException;
int indexOf(Advisor advisor);
boolean removeAdvisor(Advisor advisor) throws AopConfigException;
void removeAdvisor(int index) throws AopConfigException;
boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;
boolean isFrozen();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
fun getAdvisors(): Array<Advisor>
@Throws(AopConfigException::class)
fun addAdvice(advice: Advice)
@Throws(AopConfigException::class)
fun addAdvice(pos: Int, advice: Advice)
@Throws(AopConfigException::class)
fun addAdvisor(advisor: Advisor)
@Throws(AopConfigException::class)
fun addAdvisor(pos: Int, advisor: Advisor)
fun indexOf(advisor: Advisor): Int
@Throws(AopConfigException::class)
fun removeAdvisor(advisor: Advisor): Boolean
@Throws(AopConfigException::class)
fun removeAdvisor(index: Int)
@Throws(AopConfigException::class)
fun replaceAdvisor(a: Advisor, b: Advisor): Boolean
fun isFrozen(): Boolean
----
======
The `getAdvisors()` method returns an `Advisor` for every advisor, interceptor, or
other advice type that has been added to the factory. If you added an `Advisor`, the
returned advisor at this index is the object that you added. If you added an
interceptor or other advice type, Spring wrapped this in an advisor with a
pointcut that always returns `true`. Thus, if you added a `MethodInterceptor`, the advisor
returned for this index is a `DefaultPointcutAdvisor` that returns your
`MethodInterceptor` and a pointcut that matches all classes and methods.
The `addAdvisor()` methods can be used to add any `Advisor`. Usually, the advisor holding
pointcut and advice is the generic `DefaultPointcutAdvisor`, which you can use with
any advice or pointcut (but not for introductions).
By default, it is possible to add or remove advisors or interceptors even once a proxy
has been created. The only restriction is that it is impossible to add or remove an
introduction advisor, as existing proxies from the factory do not show the interface
change. (You can obtain a new proxy from the factory to avoid this problem.)
The following example shows casting an AOP proxy to the `Advised` interface and examining and
manipulating its advice:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Advised advised = (Advised) myObject;
Advisor[] advisors = advised.getAdvisors();
int oldAdvisorCount = advisors.length;
System.out.println(oldAdvisorCount + " advisors");
// Add an advice like an interceptor without a pointcut
// Will match all proxied methods
// Can use for interceptors, before, after returning or throws advice
advised.addAdvice(new DebugInterceptor());
// Add selective advice using a pointcut
advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice));
assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val advised = myObject as Advised
val advisors = advised.advisors
val oldAdvisorCount = advisors.size
println("$oldAdvisorCount advisors")
// Add an advice like an interceptor without a pointcut
// Will match all proxied methods
// Can use for interceptors, before, after returning or throws advice
advised.addAdvice(DebugInterceptor())
// Add selective advice using a pointcut
advised.addAdvisor(DefaultPointcutAdvisor(mySpecialPointcut, myAdvice))
assertEquals("Added two advisors", oldAdvisorCount + 2, advised.advisors.size)
----
======
NOTE: It is questionable whether it is advisable (no pun intended) to modify advice on a
business object in production, although there are, no doubt, legitimate usage cases.
However, it can be very useful in development (for example, in tests). We have sometimes
found it very useful to be able to add test code in the form of an interceptor or other
advice, getting inside a method invocation that we want to test. (For example, the advice can
get inside a transaction created for that method, perhaps to run SQL to check that
a database was correctly updated, before marking the transaction for roll back.)
Depending on how you created the proxy, you can usually set a `frozen` flag. In that
case, the `Advised` `isFrozen()` method returns `true`, and any attempts to modify
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).
@@ -0,0 +1,20 @@
[[aop-api-advisor]]
= The Advisor API in Spring
:page-section-summary-toc: 1
In Spring, an Advisor is an aspect that contains only a single advice object associated
with a pointcut expression.
Apart from the special case of introductions, any advisor can be used with any advice.
`org.springframework.aop.support.DefaultPointcutAdvisor` is the most commonly used
advisor class. It can be used with a `MethodInterceptor`, `BeforeAdvice`, or
`ThrowsAdvice`.
It is possible to mix advisor and advice types in Spring in the same AOP proxy. For
example, you could use an interception around advice, throws advice, and before advice in
one proxy configuration. Spring automatically creates the necessary interceptor
chain.
@@ -0,0 +1,131 @@
[[aop-autoproxy]]
= Using the "auto-proxy" facility
So far, we have considered explicit creation of AOP proxies by using a `ProxyFactoryBean` or
similar factory bean.
Spring also lets us use "`auto-proxy`" bean definitions, which can automatically
proxy selected bean definitions. This is built on Spring's "`bean post processor`"
infrastructure, which enables modification of any bean definition as the container loads.
In this model, you set up some special bean definitions in your XML bean definition file
to configure the auto-proxy infrastructure. This lets you declare the targets
eligible for auto-proxying. You need not use `ProxyFactoryBean`.
There are two ways to do this:
* By using an auto-proxy creator that refers to specific beans in the current context.
* A special case of auto-proxy creation that deserves to be considered separately:
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`
The `BeanNameAutoProxyCreator` class is a `BeanPostProcessor` that automatically creates
AOP proxies for beans with names that match literal values or wildcards. The following
example shows how to create a `BeanNameAutoProxyCreator` bean:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="jdk*,onlyJdk"/>
<property name="interceptorNames">
<list>
<value>myInterceptor</value>
</list>
</property>
</bean>
----
As with `ProxyFactoryBean`, there is an `interceptorNames` property rather than a list
of interceptors, to allow correct behavior for prototype advisors. Named "`interceptors`"
can be advisors or any advice type.
As with auto-proxying in general, the main point of using `BeanNameAutoProxyCreator` is
to apply the same configuration consistently to multiple objects, with minimal volume of
configuration. It is a popular choice for applying declarative transactions to multiple
objects.
Bean definitions whose names match, such as `jdkMyBean` and `onlyJdk` in the preceding
example, are plain old bean definitions with the target class. An AOP proxy is
automatically created by the `BeanNameAutoProxyCreator`. The same advice is applied
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`
A more general and extremely powerful auto-proxy creator is
`DefaultAdvisorAutoProxyCreator`. This automagically applies eligible advisors in the
current context, without the need to include specific bean names in the auto-proxy
advisor's bean definition. It offers the same merit of consistent configuration and
avoidance of duplication as `BeanNameAutoProxyCreator`.
Using this mechanism involves:
* Specifying a `DefaultAdvisorAutoProxyCreator` bean definition.
* Specifying any number of advisors in the same or related contexts. Note that these
must be advisors, not interceptors or other advice. This is necessary,
because there must be a pointcut to evaluate, to check the eligibility of each advice
to candidate bean definitions.
The `DefaultAdvisorAutoProxyCreator` automatically evaluates the pointcut contained
in each advisor, to see what (if any) advice it should apply to each business object
(such as `businessObject1` and `businessObject2` in the example).
This means that any number of advisors can be applied automatically to each business
object. If no pointcut in any of the advisors matches any method in a business object,
the object is not proxied. As bean definitions are added for new business objects,
they are automatically proxied if necessary.
Auto-proxying in general has the advantage of making it impossible for callers or
dependencies to obtain an un-advised object. Calling `getBean("businessObject1")` on this
`ApplicationContext` returns an AOP proxy, not the target business object. (The "`inner
bean`" idiom shown earlier also offers this benefit.)
The following example creates a `DefaultAdvisorAutoProxyCreator` bean and the other
elements discussed in this section:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
<property name="transactionInterceptor" ref="transactionInterceptor"/>
</bean>
<bean id="customAdvisor" class="com.mycompany.MyAdvisor"/>
<bean id="businessObject1" class="com.mycompany.BusinessObject1">
<!-- Properties omitted -->
</bean>
<bean id="businessObject2" class="com.mycompany.BusinessObject2"/>
----
The `DefaultAdvisorAutoProxyCreator` is very useful if you want to apply the same advice
consistently to many business objects. Once the infrastructure definitions are in place,
you can add new business objects without including specific proxy configuration.
You can also easily drop in additional aspects (for example, tracing or
performance monitoring aspects) with minimal change to configuration.
The `DefaultAdvisorAutoProxyCreator` offers support for filtering (by using a naming
convention so that only certain advisors are evaluated, which allows the use of multiple,
differently configured, AdvisorAutoProxyCreators in the same factory) and ordering.
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.
@@ -0,0 +1,71 @@
[[aop-concise-proxy]]
= Concise Proxy Definitions
Especially when defining transactional proxies, you may end up with many similar proxy
definitions. The use of parent and child bean definitions, along with inner bean
definitions, can result in much cleaner and more concise proxy definitions.
First, we create a parent, template, bean definition for the proxy, as follows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="txProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
----
This is never instantiated itself, so it can actually be incomplete. Then, each proxy
that needs to be created is a child bean definition, which wraps the target of the
proxy as an inner bean definition, since the target is never used on its own anyway.
The following example shows such a child bean:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="myService" parent="txProxyTemplate">
<property name="target">
<bean class="org.springframework.samples.MyServiceImpl">
</bean>
</property>
</bean>
----
You can override properties from the parent template. In the following example,
we override the transaction propagation settings:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="mySpecialService" parent="txProxyTemplate">
<property name="target">
<bean class="org.springframework.samples.MySpecialServiceImpl">
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="store*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
----
Note that in the parent bean example, we explicitly marked the parent bean definition as
being abstract by setting the `abstract` attribute to `true`, as described
xref:core/beans/child-bean-definitions.adoc[previously], so that it may not actually ever be
instantiated. Application contexts (but not simple bean factories), by default,
pre-instantiate all singletons. Therefore, it is important (at least for singleton beans)
that, if you have a (parent) bean definition that 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 actually tries to
pre-instantiate it.
@@ -0,0 +1,16 @@
[[aop-extensibility]]
= Defining New Advice Types
:page-section-summary-toc: 1
Spring AOP is designed to be extensible. While the interception implementation strategy
is presently used internally, it is possible to support arbitrary advice types in
addition to the interception around advice, before, throws advice, and
after returning advice.
The `org.springframework.aop.framework.adapter` package is an SPI package that lets
support for new custom advice types be added without changing the core framework.
The only constraint on a custom `Advice` type is that it must implement the
`org.aopalliance.aop.Advice` marker interface.
See the {api-spring-framework}/aop/framework/adapter/package-summary.html[`org.springframework.aop.framework.adapter`]
javadoc for further information.
@@ -0,0 +1,329 @@
[[aop-pfb]]
= Using the `ProxyFactoryBean` to Create AOP Proxies
If you use the Spring IoC container (an `ApplicationContext` or `BeanFactory`) for your
business objects (and you should be!), you want to use one of Spring's AOP
`FactoryBean` implementations. (Remember that a factory bean introduces a layer of indirection, letting
it create objects of a different type.)
NOTE: The Spring AOP support also uses factory beans under the covers.
The basic way to create an AOP proxy in Spring is to use the
`org.springframework.aop.framework.ProxyFactoryBean`. This gives complete control over
the pointcuts, any advice that applies, and their ordering. However, there are simpler
options that are preferable if you do not need such control.
[[aop-pfb-1]]
== Basics
The `ProxyFactoryBean`, like other Spring `FactoryBean` implementations, introduces a
level of indirection. If you define a `ProxyFactoryBean` named `foo`, objects that
reference `foo` do not see the `ProxyFactoryBean` instance itself but an object
created by the implementation of the `getObject()` method in the `ProxyFactoryBean` . This
method creates an AOP proxy that wraps a target object.
One of the most important benefits of using a `ProxyFactoryBean` or another IoC-aware
class to create AOP proxies is that advice and pointcuts can also be
managed by IoC. This is a powerful feature, enabling certain approaches that are hard to
achieve with other AOP frameworks. For example, an advice may itself reference
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
In common with most `FactoryBean` implementations provided with Spring, the
`ProxyFactoryBean` class is itself a JavaBean. Its properties are used to:
* Specify the target you want to proxy.
* Specify whether to use CGLIB (described later and see also xref:core/aop-api/pfb.adoc#aop-pfb-proxy-types[JDK- and CGLIB-based proxies]).
Some key properties are inherited from `org.springframework.aop.framework.ProxyConfig`
(the superclass for all AOP proxy factories in Spring). These key properties include
the following:
* `proxyTargetClass`: `true` if the target class is to be proxied, rather than the
target class's interfaces. If this property value is set to `true`, then CGLIB proxies
are created (but see also xref:core/aop-api/pfb.adoc#aop-pfb-proxy-types[JDK- and CGLIB-based proxies]).
* `optimize`: Controls whether or not aggressive optimizations are applied to proxies
created through CGLIB. You should not blithely use this setting unless you fully
understand how the relevant AOP proxy handles optimization. This is currently used
only for CGLIB proxies. It has no effect with JDK dynamic proxies.
* `frozen`: If a proxy configuration is `frozen`, changes to the configuration are
no longer allowed. This is useful both as a slight optimization and for those cases
when you do not want callers to be able to manipulate the proxy (through the `Advised`
interface) after the proxy has been created. The default value of this property is
`false`, so changes (such as adding additional advice) are allowed.
* `exposeProxy`: Determines whether or not the current proxy should be exposed in a
`ThreadLocal` so that it can be accessed by the target. If a target needs to obtain
the proxy and the `exposeProxy` property is set to `true`, the target can use the
`AopContext.currentProxy()` method.
Other properties specific to `ProxyFactoryBean` include the following:
* `proxyInterfaces`: An array of `String` interface names. If this is not supplied, a CGLIB
proxy for the target class is used (but see also xref:core/aop-api/pfb.adoc#aop-pfb-proxy-types[JDK- and CGLIB-based proxies]).
* `interceptorNames`: A `String` array of `Advisor`, interceptor, or other advice names to
apply. Ordering is significant, on a first come-first served basis. That is to say
that the first interceptor in the list is the first to be able to intercept the
invocation.
+
The names are bean names in the current factory, including bean names from ancestor
factories. You cannot mention bean references here, since doing so results in the
`ProxyFactoryBean` ignoring the singleton setting of the advice.
+
You can append an interceptor name with an asterisk (`*`). Doing so results in the
application of all advisor beans with names that start with the part before the asterisk
to be applied. You can find an example of using this feature in xref:core/aop-api/pfb.adoc#aop-global-advisors[Using "`Global`" Advisors].
* singleton: Whether or not the factory should return a single object, no matter how
often the `getObject()` method is called. Several `FactoryBean` implementations offer
such a method. The default value is `true`. If you want to use stateful advice - for
example, for stateful mixins - use prototype advice along with a singleton value of
`false`.
[[aop-pfb-proxy-types]]
== JDK- and CGLIB-based proxies
This section serves as the definitive documentation on how the `ProxyFactoryBean`
chooses to create either a JDK-based proxy or a CGLIB-based proxy for a particular target
object (which is to be proxied).
NOTE: The behavior of the `ProxyFactoryBean` with regard to creating JDK- or CGLIB-based
proxies changed between versions 1.2.x and 2.0 of Spring. The `ProxyFactoryBean` now
exhibits similar semantics with regard to auto-detecting interfaces as those of the
`TransactionProxyFactoryBean` class.
If the class of a target object that is to be proxied (hereafter simply referred to as
the target class) does not implement any interfaces, a CGLIB-based proxy is
created. This is the easiest scenario, because JDK proxies are interface-based, and no
interfaces means JDK proxying is not even possible. You can plug in the target bean
and specify the list of interceptors by setting the `interceptorNames` property. Note that a
CGLIB-based proxy is created even if the `proxyTargetClass` property of the
`ProxyFactoryBean` has been set to `false`. (Doing so makes no sense and is best
removed from the bean definition, because it is, at best, redundant, and, at worst
confusing.)
If the target class implements one (or more) interfaces, the type of proxy that is
created depends on the configuration of the `ProxyFactoryBean`.
If the `proxyTargetClass` property of the `ProxyFactoryBean` has been set to `true`,
a CGLIB-based proxy is created. This makes sense and is in keeping with the
principle of least surprise. Even if the `proxyInterfaces` property of the
`ProxyFactoryBean` has been set to one or more fully qualified interface names, the fact
that the `proxyTargetClass` property is set to `true` causes CGLIB-based
proxying to be in effect.
If the `proxyInterfaces` property of the `ProxyFactoryBean` has been set to one or more
fully qualified interface names, a JDK-based proxy is created. The created
proxy implements all of the interfaces that were specified in the `proxyInterfaces`
property. If the target class happens to implement a whole lot more interfaces than
those specified in the `proxyInterfaces` property, that is all well and good, but those
additional interfaces are not implemented by the returned proxy.
If the `proxyInterfaces` property of the `ProxyFactoryBean` has not been set, but
the target class does implement one (or more) interfaces, the
`ProxyFactoryBean` auto-detects the fact that the target class does actually
implement at least one interface, and a JDK-based proxy is created. The interfaces
that are actually proxied are all of the interfaces that the target class
implements. In effect, this is the same as supplying a list of each and every
interface that the target class implements to the `proxyInterfaces` property. However,
it is significantly less work and less prone to typographical errors.
[[aop-api-proxying-intf]]
== Proxying Interfaces
Consider a simple example of `ProxyFactoryBean` in action. This example involves:
* A target bean that is proxied. This is the `personTarget` bean definition in
the example.
* An `Advisor` and an `Interceptor` used to provide advice.
* An AOP proxy bean definition to specify the target object (the `personTarget` bean),
the interfaces to proxy, and the advice to apply.
The following listing shows the example:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="personTarget" class="com.mycompany.PersonImpl">
<property name="name" value="Tony"/>
<property name="age" value="51"/>
</bean>
<bean id="myAdvisor" class="com.mycompany.MyAdvisor">
<property name="someProperty" value="Custom string property value"/>
</bean>
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor">
</bean>
<bean id="person"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.mycompany.Person"/>
<property name="target" ref="personTarget"/>
<property name="interceptorNames">
<list>
<value>myAdvisor</value>
<value>debugInterceptor</value>
</list>
</property>
</bean>
----
Note that the `interceptorNames` property takes a list of `String`, which holds the bean names of the
interceptors or advisors in the current factory. You can use advisors, interceptors, before, after
returning, and throws advice objects. The ordering of advisors is significant.
NOTE: You might be wondering why the list does not hold bean references. The reason for this is
that, if the singleton property of the `ProxyFactoryBean` is set to `false`, it must be able to
return independent proxy instances. If any of the advisors is itself a prototype, an
independent instance would need to be returned, so it is necessary to be able to obtain
an instance of the prototype from the factory. Holding a reference is not sufficient.
The `person` bean definition shown earlier can be used in place of a `Person` implementation, as
follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Person person = (Person) factory.getBean("person");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val person = factory.getBean("person") as Person;
----
======
Other beans in the same IoC context can express a strongly typed dependency on it, as
with an ordinary Java object. The following example shows how to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="personUser" class="com.mycompany.PersonUser">
<property name="person"><ref bean="person"/></property>
</bean>
----
The `PersonUser` class in this example exposes a property of type `Person`. As far as
it is concerned, the AOP proxy can be used transparently in place of a "`real`" person
implementation. However, its class would be a dynamic proxy class. It would be possible
to cast it to the `Advised` interface (discussed later).
You can conceal the distinction between target and proxy by using an anonymous
inner bean. Only the `ProxyFactoryBean` definition is different. The
advice is included only for completeness. The following example shows how to use an
anonymous inner bean:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="myAdvisor" class="com.mycompany.MyAdvisor">
<property name="someProperty" value="Custom string property value"/>
</bean>
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.mycompany.Person"/>
<!-- Use inner bean, not local reference to target -->
<property name="target">
<bean class="com.mycompany.PersonImpl">
<property name="name" value="Tony"/>
<property name="age" value="51"/>
</bean>
</property>
<property name="interceptorNames">
<list>
<value>myAdvisor</value>
<value>debugInterceptor</value>
</list>
</property>
</bean>
----
Using an anonymous inner bean has the advantage that there is only one object of type `Person`. This is useful if we want
to prevent users of the application context from obtaining a reference to the un-advised
object or need to avoid any ambiguity with Spring IoC autowiring. There is also,
arguably, an advantage in that the `ProxyFactoryBean` definition is self-contained.
However, there are times when being able to obtain the un-advised target from the
factory might actually be an advantage (for example, in certain test scenarios).
[[aop-api-proxying-class]]
== Proxying Classes
What if you need to proxy a class, rather than one or more interfaces?
Imagine that in our earlier example, there was no `Person` interface. We needed to advise
a class called `Person` that did not implement any business interface. In this case, you
can configure Spring to use CGLIB proxying rather than dynamic proxies. To do so, set the
`proxyTargetClass` property on the `ProxyFactoryBean` shown earlier to `true`. While it is best to
program to interfaces rather than classes, the ability to advise classes that do not
implement interfaces can be useful when working with legacy code. (In general, Spring
is not prescriptive. While it makes it easy to apply good practices, it avoids forcing a
particular approach.)
If you want to, you can force the use of CGLIB in any case, even if you do have
interfaces.
CGLIB proxying works by generating a subclass of the target class at runtime. Spring
configures this generated subclass to delegate method calls to the original target. The
subclass is used to implement the Decorator pattern, weaving in the advice.
CGLIB proxying should generally be transparent to users. However, there are some issues
to consider:
* `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.
NOTE: There is no need to add CGLIB to your classpath. CGLIB is repackaged and included
in the `spring-core` JAR. In other words, CGLIB-based AOP works "out of the box", as do
JDK dynamic proxies.
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
By appending an asterisk to an interceptor name, all advisors with bean names that match
the part before the asterisk are added to the advisor chain. This can come in handy
if you need to add a standard set of "`global`" advisors. The following example defines
two global advisors:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="service"/>
<property name="interceptorNames">
<list>
<value>global*</value>
</list>
</property>
</bean>
<bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>
----
@@ -0,0 +1,256 @@
[[aop-api-pointcuts]]
= Pointcut API in Spring
This section describes how Spring handles the crucial pointcut concept.
[[aop-api-concepts]]
== Concepts
Spring's pointcut model enables pointcut reuse independent of advice types. You can
target different advice with the same pointcut.
The `org.springframework.aop.Pointcut` interface is the central interface, used to
target advice to particular classes and methods. The complete interface follows:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}
----
Splitting the `Pointcut` interface into two parts allows reuse of class and method
matching parts and fine-grained composition operations (such as performing a "`union`"
with another method matcher).
The `ClassFilter` interface is used to restrict the pointcut to a given set of target
classes. If the `matches()` method always returns true, all target classes are
matched. The following listing shows the `ClassFilter` interface definition:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface ClassFilter {
boolean matches(Class clazz);
}
----
The `MethodMatcher` interface is normally more important. The complete interface follows:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface MethodMatcher {
boolean matches(Method m, Class<?> targetClass);
boolean isRuntime();
boolean matches(Method m, Class<?> targetClass, Object... args);
}
----
The `matches(Method, Class)` method is used to test whether this pointcut ever
matches a given method on a target class. This evaluation can be performed when an AOP
proxy is created to avoid the need for a test on every method invocation. If the
two-argument `matches` method returns `true` for a given method, and the `isRuntime()`
method for the MethodMatcher returns `true`, the three-argument matches method is
invoked on every method invocation. This lets a pointcut look at the arguments passed
to the method invocation immediately before the target advice starts.
Most `MethodMatcher` implementations are static, meaning that their `isRuntime()` method
returns `false`. In this case, the three-argument `matches` method is never invoked.
TIP: If possible, try to make pointcuts static, allowing the AOP framework to cache the
results of pointcut evaluation when an AOP proxy is created.
[[aop-api-pointcut-ops]]
== Operations on Pointcuts
Spring supports operations (notably, union and intersection) on pointcuts.
Union means the methods that either pointcut matches.
Intersection means the methods that both pointcuts match.
Union is usually more useful.
You can compose pointcuts by using the static methods in the
`org.springframework.aop.support.Pointcuts` class or by using the
`ComposablePointcut` class in the same package. However, using AspectJ pointcut
expressions is usually a simpler approach.
[[aop-api-pointcuts-aspectj]]
== AspectJ Expression Pointcuts
Since 2.0, the most important type of pointcut used by Spring is
`org.springframework.aop.aspectj.AspectJExpressionPointcut`. This is a pointcut that
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
Static pointcuts are based on the method and the target class and cannot take into account
the method's arguments. Static pointcuts suffice -- and are best -- for most usages.
Spring can evaluate a static pointcut only once, when a method is first invoked.
After that, there is no need to evaluate the pointcut again with each method invocation.
The rest of this section describes some of the static pointcut implementations that are
included with Spring.
[[aop-api-pointcuts-regex]]
==== Regular Expression Pointcuts
One obvious way to specify static pointcuts is regular expressions. Several AOP
frameworks besides Spring make this possible.
`org.springframework.aop.support.JdkRegexpMethodPointcut` is a generic regular
expression pointcut that uses the regular expression support in the JDK.
With the `JdkRegexpMethodPointcut` class, you can provide a list of pattern strings.
If any of these is a match, the pointcut evaluates to `true`. (As a consequence,
the resulting pointcut is effectively the union of the specified patterns.)
The following example shows how to use `JdkRegexpMethodPointcut`:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="settersAndAbsquatulatePointcut"
class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
----
Spring provides a convenience class named `RegexpMethodPointcutAdvisor`, which lets us
also reference an `Advice` (remember that an `Advice` can be an interceptor, before advice,
throws advice, and others). Behind the scenes, Spring uses a `JdkRegexpMethodPointcut`.
Using `RegexpMethodPointcutAdvisor` simplifies wiring, as the one bean encapsulates both
pointcut and advice, as the following example shows:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="settersAndAbsquatulateAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="beanNameOfAopAllianceInterceptor"/>
</property>
<property name="patterns">
<list>
<value>.*set.*</value>
<value>.*absquatulate</value>
</list>
</property>
</bean>
----
You can use `RegexpMethodPointcutAdvisor` with any `Advice` type.
[[aop-api-pointcuts-attribute-driven]]
==== Attribute-driven Pointcuts
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
Dynamic pointcuts are costlier to evaluate than static pointcuts. They take into account
method arguments as well as static information. This means that they must be
evaluated with every method invocation and that the result cannot be cached, as arguments will
vary.
The main example is the `control flow` pointcut.
[[aop-api-pointcuts-cflow]]
==== Control Flow Pointcuts
Spring control flow pointcuts are conceptually similar to AspectJ `cflow` pointcuts,
although less powerful. (There is currently no way to specify that a pointcut runs
below a join point matched by another pointcut.) A control flow pointcut matches the
current call stack. For example, it might fire if the join point was invoked by a method
in the `com.mycompany.web` package or by the `SomeCaller` class. Control flow pointcuts
are specified by using the `org.springframework.aop.support.ControlFlowPointcut` class.
NOTE: Control flow pointcuts are significantly more expensive to evaluate at runtime than even
other dynamic pointcuts. In Java 1.4, the cost is about five times that of other dynamic
pointcuts.
[[aop-api-pointcuts-superclasses]]
== Pointcut Superclasses
Spring provides useful pointcut superclasses to help you to implement your own pointcuts.
Because static pointcuts are most useful, you should probably subclass
`StaticMethodMatcherPointcut`. This requires implementing only one
abstract method (although you can override other methods to customize behavior). The
following example shows how to subclass `StaticMethodMatcherPointcut`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class TestStaticPointcut extends StaticMethodMatcherPointcut {
public boolean matches(Method m, Class targetClass) {
// return true if custom criteria match
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class TestStaticPointcut : StaticMethodMatcherPointcut() {
override fun matches(method: Method, targetClass: Class<*>): Boolean {
// return true if custom criteria match
}
}
----
======
There are also superclasses for dynamic pointcuts.
You can use custom pointcuts with any advice type.
[[aop-api-pointcuts-custom]]
== Custom Pointcuts
Because pointcuts in Spring AOP are Java classes rather than language features (as in
AspectJ), you can declare custom pointcuts, whether static or dynamic. Custom
pointcuts in Spring can be arbitrarily complex. However, we recommend using the AspectJ pointcut
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.`"
@@ -0,0 +1,54 @@
[[aop-prog]]
= Creating AOP Proxies Programmatically with the `ProxyFactory`
It is easy to create AOP proxies programmatically with Spring. This lets you use
Spring AOP without dependency on Spring IoC.
The interfaces implemented by the target object are
automatically proxied. The following listing shows creation of a proxy for a target object, with one
interceptor and one advisor:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val factory = ProxyFactory(myBusinessInterfaceImpl)
factory.addAdvice(myMethodInterceptor)
factory.addAdvisor(myAdvisor)
val tb = factory.proxy as MyBusinessInterface
----
======
The first step is to construct an object of type
`org.springframework.aop.framework.ProxyFactory`. You can create this with a target
object, as in the preceding example, or specify the interfaces to be proxied in an alternate
constructor.
You can add advice (with interceptors as a specialized kind of advice), advisors, or both
and manipulate them for the life of the `ProxyFactory`. If you add an
`IntroductionInterceptionAroundAdvisor`, you can cause the proxy to implement additional
interfaces.
There are also convenience methods on `ProxyFactory` (inherited from `AdvisedSupport`)
that let you add other advice types, such as before and throws advice.
`AdvisedSupport` is the superclass of both `ProxyFactory` and `ProxyFactoryBean`.
TIP: Integrating AOP proxy creation with the IoC framework is best practice in most
applications. We recommend that you externalize configuration from Java code with AOP,
as you should in general.
@@ -0,0 +1,232 @@
[[aop-targetsource]]
= Using `TargetSource` Implementations
Spring offers the concept of a `TargetSource`, expressed in the
`org.springframework.aop.TargetSource` interface. This interface is responsible for
returning the "`target object`" that implements the join point. The `TargetSource`
implementation is asked for a target instance each time the AOP proxy handles a method
invocation.
Developers who use Spring AOP do not normally need to work directly with `TargetSource` implementations, but
this provides a powerful means of supporting pooling, hot swappable, and other
sophisticated targets. For example, a pooling `TargetSource` can return a different target
instance for each invocation, by using a pool to manage instances.
If you do not specify a `TargetSource`, a default implementation is used to wrap a
local object. The same target is returned for each invocation (as you would expect).
The rest of this section describes the standard target sources provided with Spring and how you can use them.
TIP: When using a custom target source, your target will usually need to be a prototype
rather than a singleton bean definition. This allows Spring to create a new target
instance when required.
[[aop-ts-swap]]
== Hot-swappable Target Sources
The `org.springframework.aop.target.HotSwappableTargetSource` exists to let the target
of an AOP proxy be switched while letting callers keep their references to it.
Changing the target source's target takes effect immediately. The
`HotSwappableTargetSource` is thread-safe.
You can change the target by using the `swap()` method on HotSwappableTargetSource, as the follow example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val swapper = beanFactory.getBean("swapper") as HotSwappableTargetSource
val oldTarget = swapper.swap(newTarget)
----
======
The following example shows the required XML definitions:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="initialTarget" class="mycompany.OldTarget"/>
<bean id="swapper" class="org.springframework.aop.target.HotSwappableTargetSource">
<constructor-arg ref="initialTarget"/>
</bean>
<bean id="swappable" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetSource" ref="swapper"/>
</bean>
----
The preceding `swap()` call changes the target of the swappable bean. Clients that hold a
reference to that bean are unaware of the change but immediately start hitting
the new target.
Although this example does not add any advice (it is not necessary to add advice to
use a `TargetSource`), any `TargetSource` can be used in conjunction with
arbitrary advice.
[[aop-ts-pool]]
== Pooling Target Sources
Using a pooling target source provides a similar programming model to stateless session
EJBs, in which a pool of identical instances is maintained, with method invocations
going to free objects in the pool.
A crucial difference between Spring pooling and SLSB pooling is that Spring pooling can
be applied to any POJO. As with Spring in general, this service can be applied in a
non-invasive way.
Spring provides support for Commons Pool 2.2, which provides a
fairly efficient pooling implementation. You need the `commons-pool` Jar on your
application's classpath to use this feature. You can also subclass
`org.springframework.aop.target.AbstractPoolingTargetSource` to support any other
pooling API.
NOTE: Commons Pool 1.5+ is also supported but is deprecated as of Spring Framework 4.2.
The following listing shows an example configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject"
scope="prototype">
... properties omitted
</bean>
<bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPool2TargetSource">
<property name="targetBeanName" value="businessObjectTarget"/>
<property name="maxSize" value="25"/>
</bean>
<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetSource" ref="poolTargetSource"/>
<property name="interceptorNames" value="myInterceptor"/>
</bean>
----
Note that the target object (`businessObjectTarget` in the preceding example) must be a
prototype. This lets the `PoolingTargetSource` implementation create new instances
of the target to grow the pool as necessary. See the {api-spring-framework}/aop/target/AbstractPoolingTargetSource.html[javadoc of
`AbstractPoolingTargetSource`] and the concrete subclass you wish to use for information
about its properties. `maxSize` is the most basic and is always guaranteed to be present.
In this case, `myInterceptor` is the name of an interceptor that would need to be
defined in the same IoC context. However, you need not specify interceptors to
use pooling. If you want only pooling and no other advice, do not set the
`interceptorNames` property at all.
You can configure Spring to be able to cast any pooled object to the
`org.springframework.aop.target.PoolingConfig` interface, which exposes information
about the configuration and current size of the pool through an introduction. You
need to define an advisor similar to the following:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="poolConfigAdvisor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="poolTargetSource"/>
<property name="targetMethod" value="getPoolingConfigMixin"/>
</bean>
----
This advisor is obtained by calling a convenience method on the
`AbstractPoolingTargetSource` class, hence the use of `MethodInvokingFactoryBean`. This
advisor's name (`poolConfigAdvisor`, here) must be in the list of interceptors names in
the `ProxyFactoryBean` that exposes the pooled object.
The cast is defined as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
System.out.println("Max pool size is " + conf.getMaxSize());
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val conf = beanFactory.getBean("businessObject") as PoolingConfig
println("Max pool size is " + conf.maxSize)
----
======
NOTE: Pooling stateless service objects is not usually necessary. We do not believe it should
be the default choice, as most stateless objects are naturally thread safe, and instance
pooling is problematic if resources are cached.
Simpler pooling is available by using auto-proxying. You can set the `TargetSource` implementations
used by any auto-proxy creator.
[[aop-ts-prototype]]
== Prototype Target Sources
Setting up a "`prototype`" target source is similar to setting up a pooling `TargetSource`. In this
case, a new instance of the target is created on every method invocation. Although
the cost of creating a new object is not high in a modern JVM, the cost of wiring up the
new object (satisfying its IoC dependencies) may be more expensive. Thus, you should not
use this approach without very good reason.
To do this, you could modify the `poolTargetSource` definition shown earlier as follows
(we also changed the name, for clarity):
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource">
<property name="targetBeanName" ref="businessObjectTarget"/>
</bean>
----
The only property is the name of the target bean. Inheritance is used in the
`TargetSource` implementations to ensure consistent naming. As with the pooling target
source, the target bean must be a prototype bean definition.
[[aop-ts-threadlocal]]
== `ThreadLocal` Target Sources
`ThreadLocal` target sources are useful if you need an object to be created for each
incoming request (per thread that is). The concept of a `ThreadLocal` provides a JDK-wide
facility to transparently store a resource alongside a thread. Setting up a
`ThreadLocalTargetSource` is pretty much the same as was explained for the other types
of target source, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource">
<property name="targetBeanName" value="businessObjectTarget"/>
</bean>
----
NOTE: `ThreadLocal` instances come with serious issues (potentially resulting in memory leaks) when
incorrectly using them in multi-threaded and multi-classloader environments. You
should always consider wrapping a `ThreadLocal` in some other class and never directly use
the `ThreadLocal` itself (except in the wrapper class). Also, you should
always remember to correctly set and unset (where the latter simply involves a call to
`ThreadLocal.set(null)`) the resource local to the thread. Unsetting should be done in
any case, since not unsetting it might result in problematic behavior. Spring's
`ThreadLocal` support does this for you and should always be considered in favor of using
`ThreadLocal` instances without other proper handling code.
@@ -0,0 +1,38 @@
[[aop]]
= Aspect Oriented Programming with Spring
Aspect-oriented Programming (AOP) complements Object-oriented Programming (OOP) by
providing another way of thinking about program structure. The key unit of modularity
in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects
enable the modularization of concerns (such as transaction management) that cut across
multiple types and objects. (Such concerns are often termed "crosscutting" concerns
in AOP literature.)
One of the key components of Spring is the AOP framework. While the Spring IoC
container does not depend on AOP (meaning you do not need to use AOP if you don't want
to), AOP complements Spring IoC to provide a very capable middleware solution.
.Spring AOP with AspectJ pointcuts
****
Spring provides simple and powerful ways of writing custom aspects by using either a
xref:core/aop/schema.adoc[schema-based approach] or the xref:core/aop/ataspectj.adoc[@AspectJ annotation style].
Both of these styles offer fully typed advice and use of the AspectJ pointcut language
while still using Spring AOP for weaving.
This chapter discusses the schema- and @AspectJ-based AOP support.
The lower-level AOP support is discussed in xref:core/aop-api.adoc[the following chapter].
****
AOP is used in the Spring Framework to:
* Provide declarative enterprise services. The most important such service is
xref:data-access/transaction/declarative.adoc[declarative transaction management].
* Let users implement custom aspects, complementing their use of OOP with AOP.
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.
@@ -0,0 +1,59 @@
[[aop-aspectj-programmatic]]
= Programmatic Creation of @AspectJ Proxies
In addition to declaring aspects in your configuration by using either `<aop:config>`
or `<aop:aspectj-autoproxy>`, it is also possible to programmatically create proxies
that advise target objects. For the full details of Spring's AOP API, see the
xref:core/aop-api.adoc[next chapter]. Here, we want to focus on the ability to automatically
create proxies by using @AspectJ aspects.
You can use the `org.springframework.aop.aspectj.annotation.AspectJProxyFactory` class
to create a proxy for a target object that is advised by one or more @AspectJ aspects.
The basic usage for this class is very simple, as the following example shows:
[tabs]
======
Java::
+
[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);
// add an aspect, the class must be an @AspectJ aspect
// you can call this as many times as you need with different aspects
factory.addAspect(SecurityManager.class);
// you can also add existing aspect instances, the type of the object supplied
// must be an @AspectJ aspect
factory.addAspect(usageTracker);
// now get the proxy object...
MyInterfaceType proxy = factory.getProxy();
----
Kotlin::
+
[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)
// add an aspect, the class must be an @AspectJ aspect
// you can call this as many times as you need with different aspects
factory.addAspect(SecurityManager::class.java)
// you can also add existing aspect instances, the type of the object supplied
// must be an @AspectJ aspect
factory.addAspect(usageTracker)
// now get the proxy object...
val proxy = factory.getProxy<Any>()
----
======
See the {api-spring-framework}/aop/aspectj/annotation/AspectJProxyFactory.html[javadoc] for more information.
@@ -0,0 +1,16 @@
[[aop-ataspectj]]
= @AspectJ support
:page-section-summary-toc: 1
@AspectJ refers to a style of declaring aspects as regular Java classes annotated with
annotations. The @AspectJ style was introduced by the
https://www.eclipse.org/aspectj[AspectJ project] as part of the AspectJ 5 release. Spring
interprets the same annotations as AspectJ 5, using a library supplied by AspectJ
for pointcut parsing and matching. The AOP runtime is still pure Spring AOP, though, and
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].
@@ -0,0 +1,946 @@
[[aop-advice]]
= Declaring Advice
Advice is associated with a pointcut expression and runs before, after, or around method
executions matched by the pointcut. The pointcut expression may be either an _inline
pointcut_ or a reference to a xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[_named pointcut_].
[[aop-advice-before]]
== Before Advice
You can declare before advice in an aspect by using the `@Before` annotation.
The following example uses an inline pointcut expression.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {
@Before("execution(* com.xyz.dao.*.*(..))")
public void doAccessCheck() {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
@Aspect
class BeforeExample {
@Before("execution(* com.xyz.dao.*.*(..))")
fun doAccessCheck() {
// ...
}
}
----
======
If we use a xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[named pointcut], we can rewrite the preceding example
as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {
@Before("com.xyz.CommonPointcuts.dataAccessOperation()")
public void doAccessCheck() {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
@Aspect
class BeforeExample {
@Before("com.xyz.CommonPointcuts.dataAccessOperation()")
fun doAccessCheck() {
// ...
}
}
----
======
[[aop-advice-after-returning]]
== After Returning Advice
After returning advice runs when a matched method execution returns normally.
You can declare it by using the `@AfterReturning` annotation.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
@AfterReturning("execution(* com.xyz.dao.*.*(..))")
public void doAccessCheck() {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterReturning
@Aspect
class AfterReturningExample {
@AfterReturning("execution(* com.xyz.dao.*.*(..))")
fun doAccessCheck() {
// ...
}
}
----
======
NOTE: You can have multiple advice declarations (and other members as well),
all inside the same aspect. We show only a single advice declaration in these
examples to focus the effect of each one.
Sometimes, you need access in the advice body to the actual value that was returned.
You can use the form of `@AfterReturning` that binds the return value to get that
access, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;
@Aspect
public class AfterReturningExample {
@AfterReturning(
pointcut="execution(* com.xyz.dao.*.*(..))",
returning="retVal")
public void doAccessCheck(Object retVal) {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterReturning
@Aspect
class AfterReturningExample {
@AfterReturning(
pointcut = "execution(* com.xyz.dao.*.*(..))",
returning = "retVal")
fun doAccessCheck(retVal: Any) {
// ...
}
}
----
======
The name used in the `returning` attribute must correspond to the name of a parameter
in the advice method. When a method execution returns, the return value is passed to
the advice method as the corresponding argument value. A `returning` clause also
restricts matching to only those method executions that return a value of the
specified type (in this case, `Object`, which matches any return value).
Please note that it is not possible to return a totally different reference when
using after returning advice.
[[aop-advice-after-throwing]]
== After Throwing Advice
After throwing advice runs when a matched method execution exits by throwing an
exception. You can declare it by using the `@AfterThrowing` annotation, as the
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class AfterThrowingExample {
@AfterThrowing("execution(* com.xyz.dao.*.*(..))")
public void doRecoveryActions() {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterThrowing
@Aspect
class AfterThrowingExample {
@AfterThrowing("execution(* com.xyz.dao.*.*(..))")
fun doRecoveryActions() {
// ...
}
}
----
======
Often, you want the advice to run only when exceptions of a given type are thrown,
and you also often need access to the thrown exception in the advice body. You can
use the `throwing` attribute to both restrict matching (if desired -- use `Throwable`
as the exception type otherwise) and bind the thrown exception to an advice parameter.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class AfterThrowingExample {
@AfterThrowing(
pointcut="execution(* com.xyz.dao.*.*(..))",
throwing="ex")
public void doRecoveryActions(DataAccessException ex) {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.AfterThrowing
@Aspect
class AfterThrowingExample {
@AfterThrowing(
pointcut = "execution(* com.xyz.dao.*.*(..))",
throwing = "ex")
fun doRecoveryActions(ex: DataAccessException) {
// ...
}
}
----
======
The name used in the `throwing` attribute must correspond to the name of a parameter in
the advice method. When a method execution exits by throwing an exception, the exception
is passed to the advice method as the corresponding argument value. A `throwing` clause
also restricts matching to only those method executions that throw an exception of the
specified type (`DataAccessException`, in this case).
[NOTE]
====
Note that `@AfterThrowing` does not indicate a general exception handling callback.
Specifically, an `@AfterThrowing` advice method is only supposed to receive exceptions
from the join point (user-declared target method) itself but not from an accompanying
`@After`/`@AfterReturning` method.
====
[[aop-advice-after-finally]]
== After (Finally) Advice
After (finally) advice runs when a matched method execution exits. It is declared by
using the `@After` annotation. After advice must be prepared to handle both normal and
exception return conditions. It is typically used for releasing resources and similar
purposes. The following example shows how to use after finally advice:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;
@Aspect
public class AfterFinallyExample {
@After("execution(* com.xyz.dao.*.*(..))")
public void doReleaseLock() {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.After
@Aspect
class AfterFinallyExample {
@After("execution(* com.xyz.dao.*.*(..))")
fun doReleaseLock() {
// ...
}
}
----
======
[NOTE]
====
Note that `@After` advice in AspectJ is defined as "after finally advice", analogous
to a finally block in a try-catch statement. It will be invoked for any outcome,
normal return or exception thrown from the join point (user-declared target method),
in contrast to `@AfterReturning` which only applies to successful normal returns.
====
[[aop-ataspectj-around-advice]]
== Around Advice
The last kind of advice is _around_ advice. Around advice runs "around" a matched
method's execution. It has the opportunity to do work both before and after the method
runs and to determine when, how, and even if the method actually gets to run at all.
Around advice is often used if you need to share state before and after a method
execution in a thread-safe manner for example, starting and stopping a timer.
[TIP]
====
Always use the least powerful form of advice that meets your requirements.
For example, do not use _around_ advice if _before_ advice is sufficient for your needs.
====
Around advice is declared by annotating a method with the `@Around` annotation. The
method should declare `Object` as its return type, and the first parameter of the method
must be of type `ProceedingJoinPoint`. Within the body of the advice method, you must
invoke `proceed()` on the `ProceedingJoinPoint` in order for the underlying method to
run. Invoking `proceed()` without arguments will result in the caller's original
arguments being supplied to the underlying method when it is invoked. For advanced use
cases, there is an overloaded variant of the `proceed()` method which accepts an array of
arguments (`Object[]`). The values in the array will be used as the arguments to the
underlying method when it is invoked.
[NOTE]
====
The behavior of `proceed` when called with an `Object[]` is a little different than the
behavior of `proceed` for around advice compiled by the AspectJ compiler. For around
advice written using the traditional AspectJ language, the number of arguments passed to
`proceed` must match the number of arguments passed to the around advice (not the number
of arguments taken by the underlying join point), and the value passed to proceed in a
given argument position supplants the original value at the join point for the entity the
value was bound to (do not worry if this does not make sense right now).
The approach taken by Spring is simpler and a better match to its proxy-based,
execution-only semantics. You only need to be aware of this difference if you compile
`@AspectJ` aspects written for Spring and use `proceed` with arguments with the AspectJ
compiler and weaver. There is a way to write such aspects that is 100% compatible across
both Spring AOP and AspectJ, and this is discussed in the
xref:core/aop/ataspectj/advice.adoc#aop-ataspectj-advice-proceeding-with-the-call[following section on advice parameters].
====
The value returned by the around advice is the return value seen by the caller of the
method. For example, a simple caching aspect could return a value from a cache if it has
one or invoke `proceed()` (and return that value) if it does not. Note that `proceed`
may be invoked once, many times, or not at all within the body of the around advice. All
of these are legal.
WARNING: If you declare the return type of your around advice method as `void`, `null`
will always be returned to the caller, effectively ignoring the result of any invocation
of `proceed()`. It is therefore recommended that an around advice method declare a return
type of `Object`. The advice method should typically return the value returned from an
invocation of `proceed()`, even if the underlying method has a `void` return type.
However, the advice may optionally return a cached value, a wrapped value, or some other
value depending on the use case.
The following example shows how to use around advice:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
public class AroundExample {
@Around("execution(* com.xyz..service.*.*(..))")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.ProceedingJoinPoint
@Aspect
class AroundExample {
@Around("execution(* com.xyz..service.*.*(..))")
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any {
// start stopwatch
val retVal = pjp.proceed()
// stop stopwatch
return retVal
}
}
----
======
[[aop-ataspectj-advice-params]]
== Advice Parameters
Spring offers fully typed advice, meaning that you declare the parameters you need in the
advice signature (as we saw earlier for the returning and throwing examples) rather than
work with `Object[]` arrays all the time. We see how to make argument and other contextual
values available to the advice body later in this section. First, we take a look at how to
write generic advice that can find out about the method the advice is currently advising.
[[aop-ataspectj-advice-params-the-joinpoint]]
=== Access to the Current `JoinPoint`
Any advice method may declare, as its first parameter, a parameter of type
`org.aspectj.lang.JoinPoint`. Note that around advice is required to declare a first
parameter of type `ProceedingJoinPoint`, which is a subclass of `JoinPoint`.
The `JoinPoint` interface provides a number of useful methods:
* `getArgs()`: Returns the method arguments.
* `getThis()`: Returns the proxy object.
* `getTarget()`: Returns the target object.
* `getSignature()`: Returns a description of the method that is being advised.
* `toString()`: Prints a useful description of the method being advised.
See the https://www.eclipse.org/aspectj/doc/released/runtime-api/org/aspectj/lang/JoinPoint.html[javadoc] for more detail.
[[aop-ataspectj-advice-params-passing]]
=== Passing Parameters to Advice
We have already seen how to bind the returned value or exception value (using after
returning and after throwing advice). To make argument values available to the advice
body, you can use the binding form of `args`. If you use a parameter name in place of a
type name in an `args` expression, the value of the corresponding argument is passed as
the parameter value when the advice is invoked. An example should make this clearer.
Suppose you want to advise the execution of DAO operations that take an `Account`
object as the first parameter, and you need access to the account in the advice body.
You could write the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
public void validateAccount(Account account) {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Before("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
fun validateAccount(account: Account) {
// ...
}
----
======
The `args(account,..)` part of the pointcut expression serves two purposes. First, it
restricts matching to only those method executions where the method takes at least one
parameter, and the argument passed to that parameter is an instance of `Account`.
Second, it makes the actual `Account` object available to the advice through the `account`
parameter.
Another way of writing this is to declare a pointcut that "provides" the `Account`
object value when it matches a join point, and then refer to the named pointcut
from the advice. This would look as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Pointcut("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
private void accountDataAccessOperation(Account account) {}
@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account) {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Pointcut("execution(* com.xyz.dao.*.*(..)) && args(account,..)")
private fun accountDataAccessOperation(account: Account) {
}
@Before("accountDataAccessOperation(account)")
fun validateAccount(account: Account) {
// ...
}
----
======
See the AspectJ programming guide for more details.
The proxy object (`this`), target object (`target`), and annotations (`@within`,
`@target`, `@annotation`, and `@args`) can all be bound in a similar fashion. The next
set of examples shows how to match the execution of methods annotated with an
`@Auditable` annotation and extract the audit code:
The following shows the definition of the `@Auditable` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable {
AuditCode value();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class Auditable(val value: AuditCode)
----
======
The following shows the advice that matches the execution of `@Auditable` methods:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Before("com.xyz.Pointcuts.publicMethod() && @annotation(auditable)") // <1>
public void audit(Auditable auditable) {
AuditCode code = auditable.value();
// ...
}
----
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Before("com.xyz.Pointcuts.publicMethod() && @annotation(auditable)") // <1>
fun audit(auditable: Auditable) {
val code = auditable.value()
// ...
}
----
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
======
[[aop-ataspectj-advice-params-generics]]
=== Advice Parameters and Generics
Spring AOP can handle generics used in class declarations and method parameters. Suppose
you have a generic type like the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
public interface Sample<T> {
void sampleGenericMethod(T param);
void sampleGenericCollectionMethod(Collection<T> param);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
interface Sample<T> {
fun sampleGenericMethod(param: T)
fun sampleGenericCollectionMethod(param: Collection<T>)
}
----
======
You can restrict interception of method types to certain parameter types by
tying the advice parameter to the parameter type for which you want to intercept the method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
public void beforeSampleMethod(MyType param) {
// Advice implementation
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
fun beforeSampleMethod(param: MyType) {
// Advice implementation
}
----
======
This approach does not work for generic collections. So you cannot define a
pointcut as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
public void beforeSampleMethod(Collection<MyType> param) {
// Advice implementation
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
fun beforeSampleMethod(param: Collection<MyType>) {
// Advice implementation
}
----
======
To make this work, we would have to inspect every element of the collection, which is not
reasonable, as we also cannot decide how to treat `null` values in general. To achieve
something similar to this, you have to type the parameter to `Collection<?>` and manually
check the type of the elements.
[[aop-ataspectj-advice-params-names]]
=== Determining Argument Names
Parameter binding in advice invocations relies on matching the names used in pointcut
expressions to the parameter names declared in advice and pointcut method signatures.
NOTE: This section uses the terms _argument_ and _parameter_ interchangeably, since
AspectJ APIs refer to parameter names as argument names.
Spring AOP uses the following `ParameterNameDiscoverer` implementations to determine
parameter names. Each discoverer will be given a chance to discover parameter names, and
the first successful discoverer wins. If none of the registered discoverers is capable
of determining parameter names, an exception will be thrown.
`AspectJAnnotationParameterNameDiscoverer` :: Uses parameter names that have been explicitly
specified by the user via the `argNames` attribute in the corresponding advice or
pointcut annotation. See xref:core/aop/ataspectj/advice.adoc#aop-ataspectj-advice-params-names-explicit[Explicit Argument Names] for details.
`KotlinReflectionParameterNameDiscoverer` :: Uses Kotlin reflection APIs to determine
parameter names. This discoverer is only used if such APIs are present on the classpath.
`StandardReflectionParameterNameDiscoverer` :: Uses the standard `java.lang.reflect.Parameter`
API to determine parameter names. Requires that code be compiled with the `-parameters`
flag for `javac`. Recommended approach on Java 8+.
`LocalVariableTableParameterNameDiscoverer` :: Analyzes the local variable table available
in the byte code of the advice class to determine parameter names from debug information.
Requires that code be compiled with debug symbols (`-g:vars` at a minimum). Deprecated
as of Spring Framework 6.0 for removal in Spring Framework 6.1 in favor of compiling
code with `-parameters`. Not supported in a GraalVM native image.
`AspectJAdviceParameterNameDiscoverer` :: Deduces parameter names from the pointcut
expression, `returning`, and `throwing` clauses. See the
{api-spring-framework}/aop/aspectj/AspectJAdviceParameterNameDiscoverer.html[javadoc]
for details on the algorithm used.
[[aop-ataspectj-advice-params-names-explicit]]
=== Explicit Argument Names
@AspectJ advice and pointcut annotations have an optional `argNames` attribute that you
can use to specify the argument names of the annotated method.
[TIP]
====
If an @AspectJ aspect has been compiled by the AspectJ compiler (`ajc`) even without
debug information, you do not need to add the `argNames` attribute, since the compiler
retains the needed information.
Similarly, if an @AspectJ aspect has been compiled with `javac` using the `-parameters`
flag, you do not need to add the `argNames` attribute, since the compiler retains the
needed information.
====
The following example shows how to use the `argNames` attribute:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Before(
value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // <1>
argNames = "bean,auditable") // <2>
public void audit(Object bean, Auditable auditable) {
AuditCode code = auditable.value();
// ... use code and bean
}
----
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
<2> Declares `bean` and `auditable` as the argument names.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Before(
value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // <1>
argNames = "bean,auditable") // <2>
fun audit(bean: Any, auditable: Auditable) {
val code = auditable.value()
// ... use code and bean
}
----
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
<2> Declares `bean` and `auditable` as the argument names.
======
If the first parameter is of type `JoinPoint`, `ProceedingJoinPoint`, or
`JoinPoint.StaticPart`, you can omit the name of the parameter from the value of the
`argNames` attribute. For example, if you modify the preceding advice to receive the join
point object, the `argNames` attribute does not need to include it:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Before(
value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // <1>
argNames = "bean,auditable") // <2>
public void audit(JoinPoint jp, Object bean, Auditable auditable) {
AuditCode code = auditable.value();
// ... use code, bean, and jp
}
----
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
<2> Declares `bean` and `auditable` as the argument names.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Before(
value = "com.xyz.Pointcuts.publicMethod() && target(bean) && @annotation(auditable)", // <1>
argNames = "bean,auditable") // <2>
fun audit(jp: JoinPoint, bean: Any, auditable: Auditable) {
val code = auditable.value()
// ... use code, bean, and jp
}
----
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
<2> Declares `bean` and `auditable` as the argument names.
======
The special treatment given to the first parameter of type `JoinPoint`,
`ProceedingJoinPoint`, or `JoinPoint.StaticPart` is particularly convenient for advice
methods that do not collect any other join point context. In such situations, you may
omit the `argNames` attribute. For example, the following advice does not need to declare
the `argNames` attribute:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Before("com.xyz.Pointcuts.publicMethod()") // <1>
public void audit(JoinPoint jp) {
// ... use jp
}
----
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Before("com.xyz.Pointcuts.publicMethod()") // <1>
fun audit(jp: JoinPoint) {
// ... use jp
}
----
<1> References the `publicMethod` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-pointcuts-combining[Combining Pointcut Expressions].
======
[[aop-ataspectj-advice-proceeding-with-the-call]]
=== Proceeding with Arguments
We remarked earlier that we would describe how to write a `proceed` call with
arguments that works consistently across Spring AOP and AspectJ. The solution is
to ensure that the advice signature binds each of the method parameters in order.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Around("execution(List<Account> find*(..)) && " +
"com.xyz.CommonPointcuts.inDataAccessLayer() && " +
"args(accountHolderNamePattern)") // <1>
public Object preProcessQueryPattern(ProceedingJoinPoint pjp,
String accountHolderNamePattern) throws Throwable {
String newPattern = preProcess(accountHolderNamePattern);
return pjp.proceed(new Object[] {newPattern});
}
----
<1> References the `inDataAccessLayer` 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"]
----
@Around("execution(List<Account> find*(..)) && " +
"com.xyz.CommonPointcuts.inDataAccessLayer() && " +
"args(accountHolderNamePattern)") // <1>
fun preProcessQueryPattern(pjp: ProceedingJoinPoint,
accountHolderNamePattern: String): Any {
val newPattern = preProcess(accountHolderNamePattern)
return pjp.proceed(arrayOf<Any>(newPattern))
}
----
<1> References the `inDataAccessLayer` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
======
In many cases, you do this binding anyway (as in the preceding example).
[[aop-ataspectj-advice-ordering]]
== Advice Ordering
What happens when multiple pieces of advice all want to run at the same join point?
Spring AOP follows the same precedence rules as AspectJ to determine the order of advice
execution. The highest precedence advice runs first "on the way in" (so, given two pieces
of before advice, the one with highest precedence runs first). "On the way out" from a
join point, the highest precedence advice runs last (so, given two pieces of after
advice, the one with the highest precedence will run second).
When two pieces of advice defined in different aspects both need to run at the same
join point, unless you specify otherwise, the order of execution is undefined. You can
control the order of execution by specifying precedence. This is done in the normal
Spring way by either implementing the `org.springframework.core.Ordered` interface in
the aspect class or annotating it with the `@Order` annotation. Given two aspects, the
aspect returning the lower value from `Ordered.getOrder()` (or the annotation value) has
the higher precedence.
[NOTE]
====
Each of the distinct advice types of a particular aspect is conceptually meant to apply
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.
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
effectively be invoked after any `@AfterReturning` or `@AfterThrowing` advice methods
in the same aspect, following AspectJ's "after finally advice" semantics for `@After`.
When two pieces of the same type of advice (for example, two `@After` advice methods)
defined in the same `@Aspect` class both need to run at the same join point, the ordering
is undefined (since there is no way to retrieve the source code declaration order through
reflection for javac-compiled classes). Consider collapsing such advice methods into one
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`.
====
@@ -0,0 +1,61 @@
[[aop-aspectj-support]]
= Enabling @AspectJ Support
To use @AspectJ aspects in a Spring configuration, you need to enable Spring support for
configuring Spring AOP based on @AspectJ aspects and auto-proxying beans based on
whether or not they are advised by those aspects. By auto-proxying, we mean that, if Spring
determines that a bean is advised by one or more aspects, it automatically generates
a proxy for that bean to intercept method invocations and ensures that advice is run
as needed.
The @AspectJ support can be enabled with XML- or Java-style configuration. In either
case, you also need to ensure that AspectJ's `aspectjweaver.jar` library is on the
classpath of your application (version 1.9 or later). This library is available in the
`lib` directory of an AspectJ distribution or from the Maven Central repository.
[[aop-enable-aspectj-java]]
== Enabling @AspectJ Support with Java Configuration
To enable @AspectJ support with Java `@Configuration`, add the `@EnableAspectJAutoProxy`
annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableAspectJAutoProxy
class AppConfig
----
======
[[aop-enable-aspectj-xml]]
== Enabling @AspectJ Support with XML Configuration
To enable @AspectJ support with XML-based configuration, use the `aop:aspectj-autoproxy`
element, as the following example shows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspectj-autoproxy/>
----
This assumes that you use schema support as described in
xref:core/appendix/xsd-schemas.adoc[XML Schema-based configuration].
See xref:core/appendix/xsd-schemas.adoc#aop[the AOP schema] for how to
import the tags in the `aop` namespace.
@@ -0,0 +1,68 @@
[[aop-at-aspectj]]
= Declaring an Aspect
With @AspectJ support enabled, any bean defined in your application context with a
class that is an @AspectJ aspect (has the `@Aspect` annotation) is automatically
detected by Spring and used to configure Spring AOP. The next two examples show the
minimal steps required for a not-very-useful aspect.
The first of the two examples shows a regular bean definition in the application context
that points to a bean class that is annotated with `@Aspect`:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="myAspect" class="com.xyz.NotVeryUsefulAspect">
<!-- configure properties of the aspect here -->
</bean>
----
The second of the two examples shows the `NotVeryUsefulAspect` class definition, which is
annotated with `@Aspect`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages",fold="none"]
----
package com.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class NotVeryUsefulAspect {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages",fold="none"]
----
package com.xyz
import org.aspectj.lang.annotation.Aspect
@Aspect
class NotVeryUsefulAspect
----
======
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)
declarations.
.Autodetecting aspects through component scanning
NOTE: You can register aspect classes as regular beans in your Spring XML configuration,
via `@Bean` methods in `@Configuration` classes, or have Spring autodetect them through
classpath scanning -- the same as any other Spring-managed bean. However, note that the
`@Aspect` annotation is not sufficient for autodetection in the classpath. For that
purpose, you need to add a separate `@Component` annotation (or, alternatively, a custom
stereotype annotation that qualifies, as per the rules of Spring's component scanner).
.Advising aspects with other aspects?
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.
@@ -0,0 +1,183 @@
[[aop-ataspectj-example]]
= An AOP Example
Now that you have seen how all the constituent parts work, we can put them together to do
something useful.
The execution of business services can sometimes fail due to concurrency issues (for
example, a deadlock loser). If the operation is retried, it is likely to succeed
on the next try. For business services where it is appropriate to retry in such
conditions (idempotent operations that do not need to go back to the user for conflict
resolution), we want to transparently retry the operation to avoid the client seeing a
`PessimisticLockingFailureException`. This is a requirement that clearly cuts across
multiple services in the service layer and, hence, is ideal for implementing through an
aspect.
Because we want to retry the operation, we need to use around advice so that we can
call `proceed` multiple times. The following listing shows the basic aspect implementation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Aspect
public class ConcurrentOperationExecutor implements Ordered {
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
retry). The `maxRetries` and `order` properties are both configured by Spring. The
main action happens in the `doConcurrentOperation` around advice. Notice that, for the
moment, we apply the retry logic to each `businessService`. We try to proceed,
and if we fail with a `PessimisticLockingFailureException`, we try again, unless
we have exhausted all of our retry attempts.
The corresponding Spring configuration follows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspectj-autoproxy/>
<bean id="concurrentOperationExecutor"
class="com.xyz.service.impl.ConcurrentOperationExecutor">
<property name="maxRetries" value="3"/>
<property name="order" value="100"/>
</bean>
----
To refine the aspect so that it retries only idempotent operations, we might define the following
`Idempotent` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Retention(RetentionPolicy.RUNTIME)
// marker annotation
public @interface Idempotent {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Retention(AnnotationRetention.RUNTIME)
// marker annotation
annotation class Idempotent
----
======
We can then use the annotation to annotate the implementation of service operations. The change
to the aspect to retry only idempotent operations involves refining the pointcut
expression so that only `@Idempotent` operations match, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Around("execution(* com.xyz..service.*.*(..)) && " +
"@annotation(com.xyz.service.Idempotent)")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Around("execution(* com.xyz..service.*.*(..)) && " +
"@annotation(com.xyz.service.Idempotent)")
fun doConcurrentOperation(pjp: ProceedingJoinPoint): Any {
// ...
}
----
======
@@ -0,0 +1,65 @@
[[aop-instantiation-models]]
= Aspect Instantiation Models
NOTE: This is an advanced topic. If you are just starting out with AOP, you can safely skip
it until later.
By default, there is a single instance of each aspect within the application
context. AspectJ calls this the singleton instantiation model. It is possible to define
aspects with alternate lifecycles. Spring supports AspectJ's `perthis` and `pertarget`
instantiation models; `percflow`, `percflowbelow`, and `pertypewithin` are not currently
supported.
You can declare a `perthis` aspect by specifying a `perthis` clause in the `@Aspect`
annotation. Consider the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Aspect("perthis(execution(* com.xyz..service.*.*(..)))")
public class MyAspect {
private int someState;
@Before("execution(* com.xyz..service.*.*(..))")
public void recordServiceUsage() {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Aspect("perthis(execution(* com.xyz..service.*.*(..)))")
class MyAspect {
private val someState: Int = 0
@Before("execution(* com.xyz..service.*.*(..))")
fun recordServiceUsage() {
// ...
}
}
----
======
In the preceding example, the effect of the `perthis` clause is that one aspect instance
is created for each unique service object that performs a business service (each unique
object bound to `this` at join points matched by the pointcut expression). The aspect
instance is created the first time that a method is invoked on the service object. The
aspect goes out of scope when the service object goes out of scope. Before the aspect
instance is created, none of the advice within it runs. As soon as the aspect instance
has been created, the advice declared within it runs at matched join points, but only
when the service object is the one with which this aspect is associated. See the AspectJ
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.
@@ -0,0 +1,79 @@
[[aop-introductions]]
= Introductions
Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare
that advised objects implement a given interface, and to provide an implementation of
that interface on behalf of those objects.
You can make an introduction by using the `@DeclareParents` annotation. This annotation
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 (e.g. for statistics via JMX):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Aspect
public class UsageTracking {
@DeclareParents(value="com.xyz.service.*+", defaultImpl=DefaultUsageTracked.class)
public static UsageTracked mixin;
@Before("execution(* com.xyz..service.*.*(..)) && this(usageTracked)")
public void recordUsage(UsageTracked usageTracked) {
usageTracked.incrementUseCount();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Aspect
class UsageTracking {
companion object {
@DeclareParents(value = "com.xyz.service.*+",
defaultImpl = DefaultUsageTracked::class)
lateinit var mixin: UsageTracked
}
@Before("execution(* com.xyz..service.*.*(..)) && this(usageTracked)")
fun recordUsage(usageTracked: UsageTracked) {
usageTracked.incrementUseCount()
}
}
----
======
The interface to be implemented is determined by the type of the annotated field. The
`value` attribute of the `@DeclareParents` annotation is an AspectJ type pattern. Any
bean of a matching type implements the `UsageTracked` interface. Note that, in the
before advice of the preceding example, service beans can be directly used as
implementations of the `UsageTracked` interface. If accessing a bean programmatically,
you would write the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
UsageTracked usageTracked = context.getBean("myService", UsageTracked.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
val usageTracked = context.getBean("myService", UsageTracked.class)
----
======
@@ -0,0 +1,586 @@
[[aop-pointcuts]]
= Declaring a Pointcut
Pointcuts determine join points of interest and thus enable us to control
when advice runs. Spring AOP only supports method execution join points for Spring
beans, so you can think of a pointcut as matching the execution of methods on Spring
beans. A pointcut declaration has two parts: a signature comprising a name and any
parameters and a pointcut expression that determines exactly which method
executions we are interested in. In the @AspectJ annotation-style of AOP, a pointcut
signature is provided by a regular method definition, and the pointcut expression is
indicated by using the `@Pointcut` annotation (the method serving as the pointcut signature
must have a `void` return type).
An example may help make this distinction between a pointcut signature and a pointcut
expression clear. The following example defines a pointcut named `anyOldTransfer` that
matches the execution of any method named `transfer`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Pointcut("execution(* transfer(..))") // the pointcut expression
private void anyOldTransfer() {} // the pointcut signature
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Pointcut("execution(* transfer(..))") // the pointcut expression
private fun anyOldTransfer() {} // the pointcut signature
----
======
The pointcut expression that forms the value of the `@Pointcut` annotation is a regular
AspectJ pointcut expression. For a full discussion of AspectJ's pointcut language, see
the https://www.eclipse.org/aspectj/doc/released/progguide/index.html[AspectJ
Programming Guide] (and, for extensions, the
https://www.eclipse.org/aspectj/doc/released/adk15notebook/index.html[AspectJ 5
Developer's Notebook]) or one of the books on AspectJ (such as _Eclipse AspectJ_, by Colyer
et al., or _AspectJ in Action_, by Ramnivas Laddad).
[[aop-pointcuts-designators]]
== Supported Pointcut Designators
Spring AOP supports the following AspectJ pointcut designators (PCD) for use in pointcut
expressions:
* `execution`: For matching method execution join points. This is the primary
pointcut designator to use when working with Spring AOP.
* `within`: Limits matching to join points within certain types (the execution
of a method declared within a matching type when using Spring AOP).
* `this`: Limits matching to join points (the execution of methods when using Spring
AOP) where the bean reference (Spring AOP proxy) is an instance of the given type.
* `target`: Limits matching to join points (the execution of methods when using
Spring AOP) where the target object (application object being proxied) is an instance
of the given type.
* `args`: Limits matching to join points (the execution of methods when using Spring
AOP) where the arguments are instances of the given types.
* `@target`: Limits matching to join points (the execution of methods when using
Spring AOP) where the class of the executing object has an annotation of the given type.
* `@args`: Limits matching to join points (the execution of methods when using Spring
AOP) where the runtime type of the actual arguments passed have annotations of the
given types.
* `@within`: Limits matching to join points within types that have the given
annotation (the execution of methods declared in types with the given annotation when
using Spring AOP).
* `@annotation`: Limits matching to join points where the subject of the join point
(the method being run in Spring AOP) has the given annotation.
.Other pointcut types
****
The full AspectJ pointcut language supports additional pointcut designators that are not
supported in Spring: `call`, `get`, `set`, `preinitialization`,
`staticinitialization`, `initialization`, `handler`, `adviceexecution`, `withincode`, `cflow`,
`cflowbelow`, `if`, `@this`, and `@withincode`. Use of these pointcut designators in pointcut
expressions interpreted by Spring AOP results in an `IllegalArgumentException` being
thrown.
The set of pointcut designators supported by Spring AOP may be extended in future
releases to support more of the AspectJ pointcut designators.
****
Because Spring AOP limits matching to only method execution join points, the preceding discussion
of the pointcut designators gives a narrower definition than you can find in the
AspectJ programming guide. In addition, AspectJ itself has type-based semantics and, at
an execution join point, both `this` and `target` refer to the same object: the
object executing the method. Spring AOP is a proxy-based system and differentiates
between the proxy object itself (which is bound to `this`) and the target object behind the
proxy (which is bound to `target`).
[NOTE]
====
Due to the proxy-based nature of Spring's AOP framework, calls within the target object
are, by definition, not intercepted. For JDK proxies, only public interface method
calls on the proxy can be intercepted. With CGLIB, public and protected method calls on
the proxy are intercepted (and even package-visible methods, if necessary). However,
common interactions through proxies should always be designed through public signatures.
Note that pointcut definitions are generally matched against any intercepted method.
If a pointcut is strictly meant to be public-only, even in a CGLIB proxy scenario with
potential non-public interactions through proxies, it needs to be defined accordingly.
If your interception needs include method calls or even constructors within the target
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
before making a decision.
====
Spring AOP also supports an additional PCD named `bean`. This PCD lets you limit
the matching of join points to a particular named Spring bean or to a set of named
Spring beans (when using wildcards). The `bean` PCD has the following form:
[source,indent=0,subs="verbatim"]
----
bean(idOrNameOfBean)
----
The `idOrNameOfBean` token can be the name of any Spring bean. Limited wildcard
support that uses the `*` character is provided, so, if you establish some naming
conventions for your Spring beans, you can write a `bean` PCD expression
to select them. As is the case with other pointcut designators, the `bean` PCD can
be used with the `&&` (and), `||` (or), and `!` (negation) operators, too.
[NOTE]
====
The `bean` PCD is supported only in Spring AOP and not in
native AspectJ weaving. It is a Spring-specific extension to the standard PCDs that
AspectJ defines and is, therefore, not available for aspects declared in the `@Aspect` model.
The `bean` PCD operates at the instance level (building on the Spring bean name
concept) rather than at the type level only (to which weaving-based AOP is limited).
Instance-based pointcut designators are a special capability of Spring's
proxy-based AOP framework and its close integration with the Spring bean factory, where
it is natural and straightforward to identify specific beans by name.
====
[[aop-pointcuts-combining]]
== Combining Pointcut Expressions
You can combine pointcut expressions by using `&&,` `||` and `!`. You can also refer to
pointcut expressions by name. The following example shows three pointcut expressions:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz;
public class Pointcuts {
@Pointcut("execution(public * *(..))")
public void publicMethod() {} // <1>
@Pointcut("within(com.xyz.trading..*)")
public void inTrading() {} // <2>
@Pointcut("publicMethod() && inTrading()")
public void tradingOperation() {} // <3>
}
----
<1> `publicMethod` matches if a method execution join point represents the execution
of any public method.
<2> `inTrading` matches if a method execution is in the trading module.
<3> `tradingOperation` matches if a method execution represents any public method in the
trading module.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz
class Pointcuts {
@Pointcut("execution(public * *(..))")
fun publicMethod() {} // <1>
@Pointcut("within(com.xyz.trading..*)")
fun inTrading() {} // <2>
@Pointcut("publicMethod() && inTrading()")
fun tradingOperation() {} // <3>
}
----
<1> `publicMethod` matches if a method execution join point represents the execution
of any public method.
<2> `inTrading` matches if a method execution is in the trading module.
<3> `tradingOperation` matches if a method execution represents any public method in the
trading module.
======
It is a best practice to build more complex pointcut expressions out of smaller _named
pointcuts_, as shown above. When referring to pointcuts by name, normal Java visibility
rules apply (you can see `private` pointcuts in the same type, `protected` pointcuts in
the hierarchy, `public` pointcuts anywhere, and so on). Visibility does not affect
pointcut matching.
[[aop-common-pointcuts]]
== Sharing Named Pointcut Definitions
When working with enterprise applications, developers often have the need to refer to
modules of the application and particular sets of operations from within several aspects.
We recommend defining a dedicated class that captures commonly used _named pointcut_
expressions for this purpose. Such a class typically resembles the following
`CommonPointcuts` example (though what you name the class is up to you):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages",fold="none"]
----
package com.xyz;
import org.aspectj.lang.annotation.Pointcut;
public class CommonPointcuts {
/**
* A join point is in the web layer if the method is defined
* in a type in the com.xyz.web package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.web..*)")
public void inWebLayer() {}
/**
* A join point is in the service layer if the method is defined
* in a type in the com.xyz.service package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.service..*)")
public void inServiceLayer() {}
/**
* A join point is in the data access layer if the method is defined
* in a type in the com.xyz.dao package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.dao..*)")
public void inDataAccessLayer() {}
/**
* A business service is the execution of any method defined on a service
* interface. This definition assumes that interfaces are placed in the
* "service" package, and that implementation types are in sub-packages.
*
* If you group service interfaces by functional area (for example,
* in packages com.xyz.abc.service and com.xyz.def.service) then
* the pointcut expression "execution(* com.xyz..service.*.*(..))"
* could be used instead.
*
* Alternatively, you can write the expression using the 'bean'
* PCD, like so "bean(*Service)". (This assumes that you have
* named your Spring service beans in a consistent fashion.)
*/
@Pointcut("execution(* com.xyz..service.*.*(..))")
public void businessService() {}
/**
* A data access operation is the execution of any method defined on a
* DAO interface. This definition assumes that interfaces are placed in the
* "dao" package, and that implementation types are in sub-packages.
*/
@Pointcut("execution(* com.xyz.dao.*.*(..))")
public void dataAccessOperation() {}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages",fold="none"]
----
package com.xyz
import org.aspectj.lang.annotation.Pointcut
class CommonPointcuts {
/**
* A join point is in the web layer if the method is defined
* in a type in the com.xyz.web package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.web..*)")
fun inWebLayer() {}
/**
* A join point is in the service layer if the method is defined
* in a type in the com.xyz.service package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.service..*)")
fun inServiceLayer() {}
/**
* A join point is in the data access layer if the method is defined
* in a type in the com.xyz.dao package or any sub-package
* under that.
*/
@Pointcut("within(com.xyz.dao..*)")
fun inDataAccessLayer() {}
/**
* A business service is the execution of any method defined on a service
* interface. This definition assumes that interfaces are placed in the
* "service" package, and that implementation types are in sub-packages.
*
* If you group service interfaces by functional area (for example,
* in packages com.xyz.abc.service and com.xyz.def.service) then
* the pointcut expression "execution(* com.xyz..service.*.*(..))"
* could be used instead.
*
* Alternatively, you can write the expression using the 'bean'
* PCD, like so "bean(*Service)". (This assumes that you have
* named your Spring service beans in a consistent fashion.)
*/
@Pointcut("execution(* com.xyz..service.*.*(..))")
fun businessService() {}
/**
* A data access operation is the execution of any method defined on a
* DAO interface. This definition assumes that interfaces are placed in the
* "dao" package, and that implementation types are in sub-packages.
*/
@Pointcut("execution(* com.xyz.dao.*.*(..))")
fun dataAccessOperation() {}
}
----
======
You can refer to the pointcuts defined in such a class anywhere you need a pointcut
expression by referencing the fully-qualified name of the class combined with the
`@Pointcut` method's name. For example, to make the service layer transactional, you
could write the following which references the
`com.xyz.CommonPointcuts.businessService()` _named pointcut_:
[source,xml,indent=0,subs="verbatim"]
----
<aop:config>
<aop:advisor
pointcut="com.xyz.CommonPointcuts.businessService()"
advice-ref="tx-advice"/>
</aop:config>
<tx:advice id="tx-advice">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
----
The `<aop:config>` and `<aop:advisor>` elements are discussed in xref:core/aop/schema.adoc[Schema-based AOP Support]. The
transaction elements are discussed in xref:data-access/transaction.adoc[Transaction Management].
[[aop-pointcuts-examples]]
== Examples
Spring AOP users are likely to use the `execution` pointcut designator the most often.
The format of an execution expression follows:
[literal,indent=0,subs="verbatim"]
----
execution(modifiers-pattern?
ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
----
All parts except the returning type pattern (`ret-type-pattern` in the preceding snippet),
the name pattern, and the parameters pattern are optional. The returning type pattern determines
what the return type of the method must be in order for a join point to be matched.
`{asterisk}` is most frequently used as the returning type pattern. It matches any return
type. A fully-qualified type name matches only when the method returns the given
type. The name pattern matches the method name. You can use the `{asterisk}` wildcard as all or
part of a name pattern. If you specify a declaring type pattern,
include a trailing `.` to join it to the name pattern component.
The parameters pattern is slightly more complex: `()` matches a
method that takes no parameters, whereas `(..)` matches any number (zero or more) of parameters.
The `({asterisk})` pattern matches a method that takes one parameter of any type.
`(*,String)` matches a method that takes two parameters. The first can be of any type, while the
second must be a `String`. Consult the
https://www.eclipse.org/aspectj/doc/released/progguide/semantics-pointcuts.html[Language
Semantics] section of the AspectJ Programming Guide for more information.
The following examples show some common pointcut expressions:
* The execution of any public method:
+
[literal,indent=0,subs="verbatim"]
----
execution(public * *(..))
----
* The execution of any method with a name that begins with `set`:
+
[literal,indent=0,subs="verbatim"]
----
execution(* set*(..))
----
* The execution of any method defined by the `AccountService` interface:
+
[literal,indent=0,subs="verbatim"]
----
execution(* com.xyz.service.AccountService.*(..))
----
* The execution of any method defined in the `service` package:
+
[literal,indent=0,subs="verbatim"]
----
execution(* com.xyz.service.*.*(..))
----
* The execution of any method defined in the service package or one of its sub-packages:
+
[literal,indent=0,subs="verbatim"]
----
execution(* com.xyz.service..*.*(..))
----
* Any join point (method execution only in Spring AOP) within the service package:
+
[literal,indent=0,subs="verbatim"]
----
within(com.xyz.service.*)
----
* Any join point (method execution only in Spring AOP) within the service package or one of its
sub-packages:
+
[literal,indent=0,subs="verbatim"]
----
within(com.xyz.service..*)
----
* Any join point (method execution only in Spring AOP) where the proxy implements the
`AccountService` interface:
+
[literal,indent=0,subs="verbatim"]
----
this(com.xyz.service.AccountService)
----
+
NOTE: `this` is more commonly used in a binding form. See the section on xref:core/aop/ataspectj/advice.adoc[Declaring Advice]
for how to make the proxy object available in the advice body.
* Any join point (method execution only in Spring AOP) where the target object
implements the `AccountService` interface:
+
[literal,indent=0,subs="verbatim"]
----
target(com.xyz.service.AccountService)
----
+
NOTE: `target` is more commonly used in a binding form. See the xref:core/aop/ataspectj/advice.adoc[Declaring Advice] section
for how to make the target object available in the advice body.
* Any join point (method execution only in Spring AOP) that takes a single parameter
and where the argument passed at runtime is `Serializable`:
+
[literal,indent=0,subs="verbatim"]
----
args(java.io.Serializable)
----
+
NOTE: `args` is more commonly used in a binding form. See the xref:core/aop/ataspectj/advice.adoc[Declaring Advice] section
for how to make the method arguments available in the advice body.
+
Note that the pointcut given in this example is different from `execution(*
*(java.io.Serializable))`. The args version matches if the argument passed at runtime is
`Serializable`, and the execution version matches if the method signature declares a single
parameter of type `Serializable`.
* Any join point (method execution only in Spring AOP) where the target object has a
`@Transactional` annotation:
+
[literal,indent=0,subs="verbatim"]
----
@target(org.springframework.transaction.annotation.Transactional)
----
+
NOTE: You can also use `@target` in a binding form. See the xref:core/aop/ataspectj/advice.adoc[Declaring Advice] section for
how to make the annotation object available in the advice body.
* Any join point (method execution only in Spring AOP) where the declared type of the
target object has an `@Transactional` annotation:
+
[literal,indent=0,subs="verbatim"]
----
@within(org.springframework.transaction.annotation.Transactional)
----
+
NOTE: You can also use `@within` in a binding form. See the xref:core/aop/ataspectj/advice.adoc[Declaring Advice] section for
how to make the annotation object available in the advice body.
* Any join point (method execution only in Spring AOP) where the executing method has an
`@Transactional` annotation:
+
[literal,indent=0,subs="verbatim"]
----
@annotation(org.springframework.transaction.annotation.Transactional)
----
+
NOTE: You can also use `@annotation` in a binding form. See the xref:core/aop/ataspectj/advice.adoc[Declaring Advice] section
for how to make the annotation object available in the advice body.
* Any join point (method execution only in Spring AOP) which takes a single parameter,
and where the runtime type of the argument passed has the `@Classified` annotation:
+
[literal,indent=0,subs="verbatim"]
----
@args(com.xyz.security.Classified)
----
+
NOTE: You can also use `@args` in a binding form. See the xref:core/aop/ataspectj/advice.adoc[Declaring Advice] section
how to make the annotation object(s) available in the advice body.
* Any join point (method execution only in Spring AOP) on a Spring bean named
`tradeService`:
+
[literal,indent=0,subs="verbatim"]
----
bean(tradeService)
----
* Any join point (method execution only in Spring AOP) on Spring beans having names that
match the wildcard expression `*Service`:
+
[literal,indent=0,subs="verbatim"]
----
bean(*Service)
----
[[writing-good-pointcuts]]
== Writing Good Pointcuts
During compilation, AspectJ processes pointcuts in order to optimize matching
performance. Examining code and determining if each join point matches (statically or
dynamically) a given pointcut is a costly process. (A dynamic match means the match
cannot be fully determined from static analysis and that a test is placed in the code to
determine if there is an actual match when the code is running). On first encountering a
pointcut declaration, AspectJ rewrites it into an optimal form for the matching
process. What does this mean? Basically, pointcuts are rewritten in DNF (Disjunctive
Normal Form) and the components of the pointcut are sorted such that those components
that are cheaper to evaluate are checked first. This means you do not have to worry
about understanding the performance of various pointcut designators and may supply them
in any order in a pointcut declaration.
However, AspectJ can work only with what it is told. For optimal performance of
matching, you should think about what you are trying to achieve and narrow the search
space for matches as much as possible in the definition. The existing designators
naturally fall into one of three groups: kinded, scoping, and contextual:
* Kinded designators select a particular kind of join point:
`execution`, `get`, `set`, `call`, and `handler`.
* Scoping designators select a group of join points of interest
(probably of many kinds): `within` and `withincode`
* Contextual designators match (and optionally bind) based on context:
`this`, `target`, and `@annotation`
A well written pointcut should include at least the first two types (kinded and
scoping). You can include the contextual designators to match based on
join point context or bind that context for use in the advice. Supplying only a
kinded designator or only a contextual designator works but could affect weaving
performance (time and memory used), due to extra processing and analysis. Scoping
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.
@@ -0,0 +1,113 @@
[[aop-choosing]]
= Choosing which AOP Declaration Style to Use
Once you have decided that an aspect is the best approach for implementing a given
requirement, how do you decide between using Spring AOP or AspectJ and between the
Aspect language (code) style, the @AspectJ annotation style, or the Spring XML style? These
decisions are influenced by a number of factors including application requirements,
development tools, and team familiarity with AOP.
[[aop-spring-or-aspectj]]
== Spring AOP or Full AspectJ?
Use the simplest thing that can work. Spring AOP is simpler than using full AspectJ, as
there is no requirement to introduce the AspectJ compiler / weaver into your development
and build processes. If you only need to advise the execution of operations on Spring
beans, Spring AOP is the right choice. If you need to advise objects not managed by
the Spring container (such as domain objects, typically), you need to use
AspectJ. You also need to use AspectJ if you wish to advise join points other than
simple method executions (for example, field get or set join points and so on).
When you use AspectJ, you have the choice of the AspectJ language syntax (also known as
the "code style") or the @AspectJ annotation style. If aspects play a large
role in your design, and you are able to use the https://www.eclipse.org/ajdt/[AspectJ
Development Tools (AJDT)] plugin for Eclipse, the AspectJ language syntax is the
preferred option. It is cleaner and simpler because the language was purposefully
designed for writing aspects. If you do not use Eclipse or have only a few aspects
that do not play a major role in your application, you may want to consider using
the @AspectJ style, sticking with regular Java compilation in your IDE, and adding
an aspect weaving phase to your build script.
[[aop-ataspectj-or-xml]]
== @AspectJ or XML for Spring AOP?
If you have chosen to use Spring AOP, you have a choice of @AspectJ or XML style.
There are various tradeoffs to consider.
The XML style may be most familiar to existing Spring users, and it is backed by genuine
POJOs. When using AOP as a tool to configure enterprise services, XML can be a good
choice (a good test is whether you consider the pointcut expression to be a part of your
configuration that you might want to change independently). With the XML style, it is
arguably clearer from your configuration which aspects are present in the system.
The XML style has two disadvantages. First, it does not fully encapsulate the
implementation of the requirement it addresses in a single place. The DRY principle says
that there should be a single, unambiguous, authoritative representation of any piece of
knowledge within a system. When using the XML style, the knowledge of how a requirement
is implemented is split across the declaration of the backing bean class and the XML in
the configuration file. When you use the @AspectJ style, this information is encapsulated
in a single module: the aspect. Secondly, the XML style is slightly more limited in what
it can express than the @AspectJ style: Only the "singleton" aspect instantiation model
is supported, and it is not possible to combine named pointcuts declared in XML.
For example, in the @AspectJ style you can write something like the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Pointcut("execution(* get*())")
public void propertyAccess() {}
@Pointcut("execution(com.xyz.Account+ *(..))")
public void operationReturningAnAccount() {}
@Pointcut("propertyAccess() && operationReturningAnAccount()")
public void accountPropertyAccess() {}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Pointcut("execution(* get*())")
fun propertyAccess() {}
@Pointcut("execution(com.xyz.Account+ *(..))")
fun operationReturningAnAccount() {}
@Pointcut("propertyAccess() && operationReturningAnAccount()")
fun accountPropertyAccess() {}
----
======
In the XML style you can declare the first two pointcuts:
[source,xml,indent=0,subs="verbatim"]
----
<aop:pointcut id="propertyAccess"
expression="execution(* get*())"/>
<aop:pointcut id="operationReturningAnAccount"
expression="execution(com.xyz.Account+ *(..))"/>
----
The downside of the XML approach is that you cannot define the
`accountPropertyAccess` pointcut by combining these definitions.
The @AspectJ style supports additional instantiation models and richer pointcut
composition. It has the advantage of keeping the aspect as a modular unit. It also has
the advantage that the @AspectJ aspects can be understood (and thus consumed) both by
Spring AOP and by AspectJ. So, if you later decide you need the capabilities of AspectJ
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.
@@ -0,0 +1,79 @@
[[aop-introduction-defn]]
= AOP Concepts
Let us begin by defining some central AOP concepts and terminology. These terms are not
Spring-specific. Unfortunately, AOP terminology is not particularly intuitive.
However, it would be even more confusing if Spring used its own terminology.
* Aspect: A modularization of a concern that cuts across multiple classes.
Transaction management is a good example of a crosscutting concern in enterprise Java
applications. In Spring AOP, aspects are implemented by using regular classes
(the xref:core/aop/schema.adoc[schema-based approach]) or regular classes annotated with the
`@Aspect` annotation (the xref:core/aop/ataspectj.adoc[@AspectJ style]).
* Join point: A point during the execution of a program, such as the execution of a
method or the handling of an exception. In Spring AOP, a join point always
represents a method execution.
* Advice: Action taken by an aspect at a particular join point. Different types of
advice include "around", "before", and "after" advice. (Advice types are discussed
later.) Many AOP frameworks, including Spring, model an advice as an interceptor and
maintain a chain of interceptors around the join point.
* Pointcut: A predicate that matches join points. Advice is associated with a
pointcut expression and runs at any join point matched by the pointcut (for example,
the execution of a method with a certain name). The concept of join points as matched
by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut
expression language by default.
* Introduction: Declaring additional methods or fields on behalf of a type. Spring
AOP lets you introduce new interfaces (and a corresponding implementation) to any
advised object. For example, you could use an introduction to make a bean implement an
`IsModified` interface, to simplify caching. (An introduction is known as an
inter-type declaration in the AspectJ community.)
* Target object: An object being advised by one or more aspects. Also referred to as
the "advised object". Since Spring AOP is implemented by using runtime proxies, this
object is always a proxied object.
* AOP proxy: An object created by the AOP framework in order to implement the aspect
contracts (advise method executions and so on). In the Spring Framework, an AOP proxy
is a JDK dynamic proxy or a CGLIB proxy.
* Weaving: linking aspects with other application types or objects to create an
advised object. This can be done at compile time (using the AspectJ compiler, for
example), load time, or at runtime. Spring AOP, like other pure Java AOP frameworks,
performs weaving at runtime.
Spring AOP includes the following types of advice:
* Before advice: Advice that runs before a join point but that does not have
the ability to prevent execution flow proceeding to the join point (unless it throws
an exception).
* After returning advice: Advice to be run after a join point completes
normally (for example, if a method returns without throwing an exception).
* After throwing advice: Advice to be run if a method exits by throwing an
exception.
* After (finally) advice: Advice to be run regardless of the means by which a
join point exits (normal or exceptional return).
* Around advice: Advice that surrounds a join point such as a method invocation.
This is the most powerful kind of advice. Around advice can perform custom behavior
before and after the method invocation. It is also responsible for choosing whether to
proceed to the join point or to shortcut the advised method execution by returning its
own return value or throwing an exception.
Around advice is the most general kind of advice. Since Spring AOP, like AspectJ,
provides a full range of advice types, we recommend that you use the least powerful
advice type that can implement the required behavior. For example, if you need only to
update a cache with the return value of a method, you are better off implementing an
after returning advice than an around advice, although an around advice can accomplish
the same thing. Using the most specific advice type provides a simpler programming model
with less potential for errors. For example, you do not need to invoke the `proceed()`
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 (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
it from older technologies offering only interception. Pointcuts enable advice to be
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).
@@ -0,0 +1,22 @@
[[aop-introduction-proxies]]
= AOP Proxies
:page-section-summary-toc: 1
Spring AOP defaults to using standard JDK dynamic proxies for AOP proxies. This
enables any interface (or set of interfaces) to be proxied.
Spring AOP can also use CGLIB proxies. This is necessary to proxy classes rather than
interfaces. By default, CGLIB is used if a business object does not implement an
interface. As it is good practice to program to interfaces rather than classes, business
classes normally implement one or more business interfaces. It is possible to
xref:core/aop/proxying.adoc[force the use of CGLIB], in those (hopefully rare) cases where you
need to advise a method that is not declared on an interface or where you need to
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.
@@ -0,0 +1,61 @@
[[aop-introduction-spring-defn]]
= Spring AOP Capabilities and Goals
Spring AOP is implemented in pure Java. There is no need for a special compilation
process. Spring AOP does not need to control the class loader hierarchy and is thus
suitable for use in a servlet container or application server.
Spring AOP currently supports only method execution join points (advising the execution
of methods on Spring beans). Field interception is not implemented, although support for
field interception could be added without breaking the core Spring AOP APIs. If you need
to advise field access and update join points, consider a language such as AspectJ.
Spring AOP's approach to AOP differs from that of most other AOP frameworks. The aim is
not to provide the most complete AOP implementation (although Spring AOP is quite
capable). Rather, the aim is to provide a close integration between AOP implementation and
Spring IoC, to help solve common problems in enterprise applications.
Thus, for example, the Spring Framework's AOP functionality is normally used in
conjunction with the Spring IoC container. Aspects are configured by using normal bean
definition syntax (although this allows powerful "auto-proxying" capabilities). This is a
crucial difference from other AOP implementations. You cannot do some things
easily or efficiently with Spring AOP, such as advise very fine-grained objects (typically,
domain objects). AspectJ is the best choice in such cases. However, our
experience is that Spring AOP provides an excellent solution to most problems in
enterprise Java applications that are amenable to AOP.
Spring AOP never strives to compete with AspectJ to provide a comprehensive AOP
solution. We believe that both proxy-based frameworks such as Spring AOP and full-blown
frameworks such as AspectJ are valuable and that they are complementary, rather than in
competition. Spring seamlessly integrates Spring AOP and IoC with AspectJ, to enable
all uses of AOP within a consistent Spring-based application
architecture. This integration does not affect the Spring AOP API or the AOP Alliance
API. Spring AOP remains backward-compatible. See xref:core/aop-api.adoc[the following chapter]
for a discussion of the Spring AOP APIs.
[NOTE]
====
One of the central tenets of the Spring Framework is that of non-invasiveness. This
is the idea that you should not be forced to introduce framework-specific classes and
interfaces into your business or domain model. However, in some places, the Spring Framework
does give you the option to introduce Spring Framework-specific dependencies into your
codebase. The rationale in giving you such options is because, in certain scenarios, it
might be just plain easier to read or code some specific piece of functionality in such
a way. However, the Spring Framework (almost) always offers you the choice: You have the
freedom to make an informed decision as to which option best suits your particular use
case or scenario.
One such choice that is relevant to this chapter is that of which AOP framework (and
which AOP style) to choose. You have the choice of AspectJ, Spring AOP, or both. You
also have the choice of either the @AspectJ annotation-style approach or the Spring XML
configuration-style approach. The fact that this chapter chooses to introduce the
@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.
====
@@ -0,0 +1,12 @@
[[aop-mixing-styles]]
= Mixing Aspect Types
:page-section-summary-toc: 1
It is perfectly possible to mix @AspectJ style aspects by using the auto-proxying support,
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.
@@ -0,0 +1,281 @@
[[aop-proxying]]
= Proxying Mechanisms
Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given
target object. JDK dynamic proxies are built into the JDK, whereas CGLIB is a common
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. 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:
* 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.
To force the use of CGLIB proxies, set the value of the `proxy-target-class` attribute
of the `<aop:config>` element to true, as follows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:config proxy-target-class="true">
<!-- other beans defined here... -->
</aop:config>
----
To force CGLIB proxying when you use the @AspectJ auto-proxy support, set the
`proxy-target-class` attribute of the `<aop:aspectj-autoproxy>` element to `true`,
as follows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspectj-autoproxy proxy-target-class="true"/>
----
[NOTE]
====
Multiple `<aop:config/>` sections are collapsed into a single unified auto-proxy creator
at runtime, which applies the _strongest_ proxy settings that any of the
`<aop:config/>` sections (typically from different XML bean definition files) specified.
This also applies to the `<tx:annotation-driven/>` and `<aop:aspectj-autoproxy/>`
elements.
To be clear, using `proxy-target-class="true"` on `<tx:annotation-driven/>`,
`<aop:aspectj-autoproxy/>`, or `<aop:config/>` elements forces the use of CGLIB
proxies _for all three of them_.
====
[[aop-understanding-aop-proxies]]
== Understanding AOP Proxies
Spring AOP is proxy-based. It is vitally important that you grasp the semantics of
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,
nothing-special-about-it, straight object reference, as the following
code snippet shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
public class SimplePojo implements Pojo {
public void foo() {
// this next method invocation is a direct call on the 'this' reference
this.bar();
}
public void bar() {
// some logic...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
class SimplePojo : Pojo {
fun foo() {
// this next method invocation is a direct call on the 'this' reference
this.bar()
}
fun bar() {
// some logic...
}
}
----
======
If you invoke a method on an object reference, the method is invoked directly on
that object reference, as the following image and listing show:
image::aop-proxy-plain-pojo-call.png[]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
public class Main {
public static void main(String[] args) {
Pojo pojo = new SimplePojo();
// this is a direct method call on the 'pojo' reference
pojo.foo();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun main() {
val pojo = SimplePojo()
// this is a direct method call on the 'pojo' reference
pojo.foo()
}
----
======
Things change slightly when the reference that client code has is a proxy. Consider the
following diagram and code snippet:
image::aop-proxy-call.png[]
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
public class Main {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
Pojo pojo = (Pojo) factory.getProxy();
// this is a method call on the proxy!
pojo.foo();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun main() {
val factory = ProxyFactory(SimplePojo())
factory.addInterface(Pojo::class.java)
factory.addAdvice(RetryAdvice())
val pojo = factory.proxy as Pojo
// this is a method call on the proxy!
pojo.foo()
}
----
======
The key thing to understand here is that the client code inside the `main(..)` method
of the `Main` class has a reference to the proxy. This means that method calls on that
object reference are calls on the proxy. As a result, the proxy can delegate to all of
the interceptors (advice) that are relevant to that particular method call. However,
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.
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",role="primary"]
----
public class SimplePojo implements Pojo {
public void foo() {
// this works, but... gah!
((Pojo) AopContext.currentProxy()).bar();
}
public void bar() {
// some logic...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
class SimplePojo : Pojo {
fun foo() {
// this works, but... gah!
(AopContext.currentProxy() as Pojo).bar()
}
fun bar() {
// some logic...
}
}
----
======
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",role="primary"]
----
public class Main {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
factory.setExposeProxy(true);
Pojo pojo = (Pojo) factory.getProxy();
// this is a method call on the proxy!
pojo.foo();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun main() {
val factory = ProxyFactory(SimplePojo())
factory.addInterface(Pojo::class.java)
factory.addAdvice(RetryAdvice())
factory.isExposeProxy = true
val pojo = factory.proxy as Pojo
// this is a method call on the proxy!
pojo.foo()
}
----
======
Finally, it must be noted that AspectJ does not have this self-invocation issue because
it is not a proxy-based AOP framework.
@@ -0,0 +1,12 @@
[[aop-resources]]
= Further Resources
:page-section-summary-toc: 1
More information on AspectJ can be found on the https://www.eclipse.org/aspectj[AspectJ website].
_Eclipse AspectJ_ by Adrian Colyer et. al. (Addison-Wesley, 2005) provides a
comprehensive introduction and reference for the AspectJ language.
_AspectJ in Action_, Second Edition by Ramnivas Laddad (Manning, 2009) comes highly
recommended. The focus of the book is on AspectJ, but a lot of general AOP themes are
explored (in some depth).
@@ -0,0 +1,987 @@
[[aop-schema]]
= Schema-based AOP Support
If you prefer an XML-based format, Spring also offers support for defining aspects
using the `aop` namespace tags. The exact same pointcut expressions and advice kinds
as when using the @AspectJ style are supported. Hence, in this section we focus on
that syntax and refer the reader to the discussion in the previous section
(xref:core/aop/ataspectj.adoc[@AspectJ support]) for an understanding of writing pointcut expressions and the binding
of advice parameters.
To use the aop namespace tags described in this section, you need to import the
`spring-aop` schema, 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.
Within your Spring configurations, all aspect and advisor elements must be placed within
an `<aop:config>` element (you can have more than one `<aop:config>` element in an
application context configuration). An `<aop:config>` element can contain pointcut,
advisor, and aspect elements (note that these must be declared in that order).
WARNING: The `<aop:config>` style of configuration makes heavy use of Spring's
xref:core/aop-api/autoproxy.adoc[auto-proxying] mechanism. This can cause issues (such as advice
not being woven) if you already use explicit auto-proxying through the use of
`BeanNameAutoProxyCreator` or something similar. The recommended usage pattern is to
use either only the `<aop:config>` style or only the `AutoProxyCreator` style and
never mix them.
[[aop-schema-declaring-an-aspect]]
== Declaring an Aspect
When you use the schema support, an aspect is a regular Java object defined as a bean in
your Spring application context. The state and behavior are captured in the fields and
methods of the object, and the pointcut and advice information are captured in the XML.
You can declare an aspect by using the `<aop:aspect>` element, and reference the backing bean
by using the `ref` attribute, as the following example shows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
----
The bean that backs the aspect (`aBean` in this case) can of course be configured and
dependency injected just like any other Spring bean.
[[aop-schema-pointcuts]]
== Declaring a Pointcut
You can declare a _named pointcut_ inside an `<aop:config>` element, letting the pointcut
definition be shared across several aspects and advisors.
A pointcut that represents the execution of any business service in the service layer can
be defined as follows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:config>
<aop:pointcut id="businessService"
expression="execution(* com.xyz.service.*.*(..))" />
</aop:config>
----
Note that the pointcut expression itself uses the same AspectJ pointcut expression
language as described in xref:core/aop/ataspectj.adoc[@AspectJ support]. If you use the schema based declaration
style, you can also refer to _named pointcuts_ defined in `@Aspect` types within the
pointcut expression. Thus, another way of defining the above pointcut would be as follows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:config>
<aop:pointcut id="businessService"
expression="com.xyz.CommonPointcuts.businessService()" /> <1>
</aop:config>
----
<1> References the `businessService` named pointcut defined in xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions].
Declaring a pointcut _inside_ an aspect is very similar to declaring a top-level pointcut,
as the following example shows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.service.*.*(..))"/>
...
</aop:aspect>
</aop:config>
----
In much the same way as an @AspectJ aspect, pointcuts declared by using the schema based
definition style can collect join point context. For example, the following pointcut
collects the `this` object as the join point context and passes it to the advice:
[source,xml,indent=0,subs="verbatim"]
----
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.service.*.*(..)) &amp;&amp; this(service)"/>
<aop:before pointcut-ref="businessService" method="monitor"/>
...
</aop:aspect>
</aop:config>
----
The advice must be declared to receive the collected join point context by including
parameters of the matching names, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
public void monitor(Object service) {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun monitor(service: Any) {
// ...
}
----
======
When combining pointcut sub-expressions, `+&amp;&amp;+` is awkward within an XML
document, so you can use the `and`, `or`, and `not` keywords in place of `+&amp;&amp;+`,
`||`, and `!`, respectively. For example, the previous pointcut can be better written as
follows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.service.*.*(..)) and this(service)"/>
<aop:before pointcut-ref="businessService" method="monitor"/>
...
</aop:aspect>
</aop:config>
----
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.
[[aop-schema-advice]]
== Declaring Advice
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
Before advice runs before a matched method execution. It is declared inside an
`<aop:aspect>` by using the `<aop:before>` element, as the following example shows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspect id="beforeExample" ref="aBean">
<aop:before
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>
...
</aop:aspect>
----
In the example above, `dataAccessOperation` is the `id` of a _named pointcut_ defined at
the top (`<aop:config>`) level (see xref:core/aop/schema.adoc#aop-schema-pointcuts[Declaring a Pointcut]).
NOTE: As we noted in the discussion of the @AspectJ style, using _named pointcuts_ can
significantly improve the readability of your code. See xref:core/aop/ataspectj/pointcuts.adoc#aop-common-pointcuts[Sharing Named Pointcut Definitions] for
details.
To define the pointcut inline instead, replace the `pointcut-ref` attribute with a
`pointcut` attribute, as follows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspect id="beforeExample" ref="aBean">
<aop:before
pointcut="execution(* com.xyz.dao.*.*(..))"
method="doAccessCheck"/>
...
</aop:aspect>
----
The `method` attribute identifies a method (`doAccessCheck`) that provides the body of
the advice. This method must be defined for the bean referenced by the aspect element
that contains the advice. Before a data access operation is performed (a method execution
join point matched by the pointcut expression), the `doAccessCheck` method on the aspect
bean is invoked.
[[aop-schema-advice-after-returning]]
=== After Returning Advice
After returning advice runs when a matched method execution completes normally. It is
declared inside an `<aop:aspect>` in the same way as before advice. The following example
shows how to declare it:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspect id="afterReturningExample" ref="aBean">
<aop:after-returning
pointcut="execution(* com.xyz.dao.*.*(..))"
method="doAccessCheck"/>
...
</aop:aspect>
----
As in the @AspectJ style, you can get the return value within the advice body.
To do so, use the `returning` attribute to specify the name of the parameter to which
the return value should be passed, as the following example shows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspect id="afterReturningExample" ref="aBean">
<aop:after-returning
pointcut="execution(* com.xyz.dao.*.*(..))"
returning="retVal"
method="doAccessCheck"/>
...
</aop:aspect>
----
The `doAccessCheck` method must declare a parameter named `retVal`. The type of this
parameter constrains matching in the same way as described for `@AfterReturning`. For
example, you can declare the method signature as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
public void doAccessCheck(Object retVal) {...
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun doAccessCheck(retVal: Any) {...
----
======
[[aop-schema-advice-after-throwing]]
=== After Throwing Advice
After throwing advice runs when a matched method execution exits by throwing an
exception. It is declared inside an `<aop:aspect>` by using the `after-throwing` element,
as the following example shows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspect id="afterThrowingExample" ref="aBean">
<aop:after-throwing
pointcut="execution(* com.xyz.dao.*.*(..))"
method="doRecoveryActions"/>
...
</aop:aspect>
----
As in the @AspectJ style, you can get the thrown exception within the advice body.
To do so, use the `throwing` attribute to specify the name of the parameter to
which the exception should be passed as the following example shows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspect id="afterThrowingExample" ref="aBean">
<aop:after-throwing
pointcut="execution(* com.xyz.dao.*.*(..))"
throwing="dataAccessEx"
method="doRecoveryActions"/>
...
</aop:aspect>
----
The `doRecoveryActions` method must declare a parameter named `dataAccessEx`.
The type of this parameter constrains matching in the same way as described for
`@AfterThrowing`. For example, the method signature may be declared as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
public void doRecoveryActions(DataAccessException dataAccessEx) {...
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun doRecoveryActions(dataAccessEx: DataAccessException) {...
----
======
[[aop-schema-advice-after-finally]]
=== After (Finally) Advice
After (finally) advice runs no matter how a matched method execution exits.
You can declare it by using the `after` element, as the following example shows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspect id="afterFinallyExample" ref="aBean">
<aop:after
pointcut="execution(* com.xyz.dao.*.*(..))"
method="doReleaseLock"/>
...
</aop:aspect>
----
[[aop-schema-advice-around]]
=== Around Advice
The last kind of advice is _around_ advice. Around advice runs "around" a matched
method's execution. It has the opportunity to do work both before and after the method
runs and to determine when, how, and even if the method actually gets to run at all.
Around advice is often used if you need to share state before and after a method
execution in a thread-safe manner for example, starting and stopping a timer.
[TIP]
====
Always use the least powerful form of advice that meets your requirements.
For example, do not use _around_ advice if _before_ advice is sufficient for your needs.
====
You can declare around advice by using the `aop:around` element. The advice method should
declare `Object` as its return type, and the first parameter of the method must be of
type `ProceedingJoinPoint`. Within the body of the advice method, you must invoke
`proceed()` on the `ProceedingJoinPoint` in order for the underlying method to run.
Invoking `proceed()` without arguments will result in the caller's original arguments
being supplied to the underlying method when it is invoked. For advanced use cases, there
is an overloaded variant of the `proceed()` method which accepts an array of arguments
(`Object[]`). The values in the array will be used as the arguments to the underlying
method when it is invoked. See xref:core/aop/ataspectj/advice.adoc#aop-ataspectj-around-advice[Around Advice] for notes on calling
`proceed` with an `Object[]`.
The following example shows how to declare around advice in XML:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspect id="aroundExample" ref="aBean">
<aop:around
pointcut="execution(* com.xyz.service.*.*(..))"
method="doBasicProfiling"/>
...
</aop:aspect>
----
The implementation of the `doBasicProfiling` advice can be exactly the same as in the
@AspectJ example (minus the annotation, of course), as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun doBasicProfiling(pjp: ProceedingJoinPoint): Any {
// start stopwatch
val retVal = pjp.proceed()
// stop stopwatch
return pjp.proceed()
}
----
======
[[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
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]).
The following example shows how to specify an argument name in XML:
[source,xml,indent=0,subs="verbatim"]
----
<aop:before
pointcut="com.xyz.Pointcuts.publicMethod() and @annotation(auditable)" <1>
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].
The `arg-names` attribute accepts a comma-delimited list of parameter names.
The following slightly more involved example of the XSD-based approach shows
some around advice used in conjunction with a number of strongly typed parameters:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz.service;
public interface PersonService {
Person getPerson(String personName, int age);
}
public class DefaultPersonService implements PersonService {
public Person getPerson(String name, int age) {
return new Person(name, age);
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz.service
interface PersonService {
fun getPerson(personName: String, age: Int): Person
}
class DefaultPersonService : PersonService {
fun getPerson(name: String, age: Int): Person {
return Person(name, age)
}
}
----
======
Next up is the aspect. Notice the fact that the `profile(..)` method accepts a number of
strongly-typed parameters, the first of which happens to be the join point used to
proceed with the method call. The presence of this parameter is an indication that the
`profile(..)` is to be used as `around` advice, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;
public class SimpleProfiler {
public Object profile(ProceedingJoinPoint call, String name, int age) throws Throwable {
StopWatch clock = new StopWatch("Profiling for '" + name + "' and '" + age + "'");
try {
clock.start(call.toShortString());
return call.proceed();
} finally {
clock.stop();
System.out.println(clock.prettyPrint());
}
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz
import org.aspectj.lang.ProceedingJoinPoint
import org.springframework.util.StopWatch
class SimpleProfiler {
fun profile(call: ProceedingJoinPoint, name: String, age: Int): Any {
val clock = StopWatch("Profiling for '$name' and '$age'")
try {
clock.start(call.toShortString())
return call.proceed()
} finally {
clock.stop()
println(clock.prettyPrint())
}
}
}
----
======
Finally, the following example XML configuration effects the execution of the
preceding advice for a particular join point:
[source,xml,indent=0,subs="verbatim"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- this is the object that will be proxied by Spring's AOP infrastructure -->
<bean id="personService" class="com.xyz.service.DefaultPersonService"/>
<!-- this is the actual advice itself -->
<bean id="profiler" class="com.xyz.SimpleProfiler"/>
<aop:config>
<aop:aspect ref="profiler">
<aop:pointcut id="theExecutionOfSomePersonServiceMethod"
expression="execution(* com.xyz.service.PersonService.getPerson(String,int))
and args(name, age)"/>
<aop:around pointcut-ref="theExecutionOfSomePersonServiceMethod"
method="profile"/>
</aop:aspect>
</aop:config>
</beans>
----
Consider the following driver script:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
public class Boot {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
PersonService person = ctx.getBean(PersonService.class);
person.getPerson("Pengo", 12);
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun main() {
val ctx = ClassPathXmlApplicationContext("beans.xml")
val person = ctx.getBean(PersonService.class)
person.getPerson("Pengo", 12)
}
----
======
With such a `Boot` class, we would get output similar to the following on standard output:
[literal,subs="verbatim"]
----
StopWatch 'Profiling for 'Pengo' and '12': running time (millis) = 0
-----------------------------------------
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.
[NOTE]
====
In contrast to the precedence rules for advice methods defined in the same `@Aspect`
class, when two pieces of advice defined in the same `<aop:aspect>` element both need to
run at the same join point, the precedence is determined by the order in which the advice
elements are declared within the enclosing `<aop:aspect>` element, from highest to lowest
precedence.
For example, given an `around` advice and a `before` advice defined in the same
`<aop:aspect>` element that apply to the same join point, to ensure that the `around`
advice has higher precedence than the `before` advice, the `<aop:around>` element must be
declared before the `<aop:before>` element.
As a general rule of thumb, if you find that you have multiple pieces of advice defined
in the same `<aop:aspect>` element that apply to the same join point, consider collapsing
such advice methods into one advice method per join point in each `<aop:aspect>` element
or refactor the pieces of advice into separate `<aop:aspect>` elements that you can order
at the aspect level.
====
[[aop-schema-introductions]]
== Introductions
Introductions (known as inter-type declarations in AspectJ) let an aspect declare
that advised objects implement a given interface and provide an implementation of
that interface on behalf of those objects.
You can make an introduction by using the `aop:declare-parents` element inside an `aop:aspect`.
You can use the `aop:declare-parents` element 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. (In order to expose statistics
through JMX for example.)
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspect id="usageTrackerAspect" ref="usageTracking">
<aop:declare-parents
types-matching="com.xyz.service.*+"
implement-interface="com.xyz.service.tracking.UsageTracked"
default-impl="com.xyz.service.tracking.DefaultUsageTracked"/>
<aop:before
pointcut="execution(* com.xyz..service.*.*(..))
and this(usageTracked)"
method="recordUsage"/>
</aop:aspect>
----
The class that backs the `usageTracking` bean would then contain the following method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
public void recordUsage(UsageTracked usageTracked) {
usageTracked.incrementUseCount();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
fun recordUsage(usageTracked: UsageTracked) {
usageTracked.incrementUseCount()
}
----
======
The interface to be implemented is determined by the `implement-interface` attribute. The
value of the `types-matching` attribute is an AspectJ type pattern. Any bean of a
matching type implements the `UsageTracked` interface. Note that, in the before
advice of the preceding example, service beans can be directly used as implementations of
the `UsageTracked` interface. To access a bean programmatically, you could write the
following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
UsageTracked usageTracked = context.getBean("myService", UsageTracked.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
val usageTracked = context.getBean("myService", UsageTracked.class)
----
======
[[aop-schema-instantiation-models]]
== Aspect Instantiation Models
The only supported instantiation model for schema-defined aspects is the singleton
model. Other instantiation models may be supported in future releases.
[[aop-schema-advisors]]
== Advisors
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.
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
namespace support in Spring. The following example shows an advisor:
[source,xml,indent=0,subs="verbatim"]
----
<aop:config>
<aop:pointcut id="businessService"
expression="execution(* com.xyz.service.*.*(..))"/>
<aop:advisor
pointcut-ref="businessService"
advice-ref="tx-advice" />
</aop:config>
<tx:advice id="tx-advice">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
----
As well as the `pointcut-ref` attribute used in the preceding example, you can also use the
`pointcut` attribute to define a pointcut expression inline.
To define the precedence of an advisor so that the advice can participate in ordering,
use the `order` attribute to define the `Ordered` value of the advisor.
[[aop-schema-example]]
== An AOP Schema Example
This section shows how the concurrent locking failure retry example from
xref:core/aop/ataspectj/example.adoc[An AOP Example] looks when rewritten with the schema support.
The execution of business services can sometimes fail due to concurrency issues (for
example, a deadlock loser). If the operation is retried, it is likely to succeed
on the next try. For business services where it is appropriate to retry in such
conditions (idempotent operations that do not need to go back to the user for conflict
resolution), we want to transparently retry the operation to avoid the client seeing a
`PessimisticLockingFailureException`. This is a requirement that clearly cuts across
multiple services in the service layer and, hence, is ideal for implementing through an
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
(which is a regular Java class that uses the schema support):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
public class ConcurrentOperationExecutor implements Ordered {
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;
}
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;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
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
}
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
}
}
----
======
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
retry). The `maxRetries` and `order` properties are both configured by Spring. The
main action happens in the `doConcurrentOperation` around advice method. We try to
proceed. If we fail with a `PessimisticLockingFailureException`, we try again,
unless we have exhausted all of our retry attempts.
NOTE: This class is identical to the one used in the @AspectJ example, but with the
annotations removed.
The corresponding Spring configuration is as follows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:config>
<aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor">
<aop:pointcut id="idempotentOperation"
expression="execution(* com.xyz.service.*.*(..))"/>
<aop:around
pointcut-ref="idempotentOperation"
method="doConcurrentOperation"/>
</aop:aspect>
</aop:config>
<bean id="concurrentOperationExecutor"
class="com.xyz.service.impl.ConcurrentOperationExecutor">
<property name="maxRetries" value="3"/>
<property name="order" value="100"/>
</bean>
----
Notice that, for the time being, we assume that all business services are idempotent. If
this is not the case, we can refine the aspect so that it retries only genuinely
idempotent operations, by introducing an `Idempotent` annotation and using the annotation
to annotate the implementation of service operations, as the following example shows:
[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
----
======
The
change to the aspect to retry only idempotent operations involves refining the
pointcut expression so that only `@Idempotent` operations match, as follows:
[source,xml,indent=0,subs="verbatim"]
----
<aop:pointcut id="idempotentOperation"
expression="execution(* com.xyz.service.*.*(..)) and
@annotation(com.xyz.service.Idempotent)"/>
----
@@ -0,0 +1,995 @@
[[aop-using-aspectj]]
= Using AspectJ with Spring Applications
Everything we have covered so far in this chapter is pure Spring AOP. In this section,
we look at how you can use the AspectJ compiler or weaver instead of or in
addition to Spring AOP if your needs go beyond the facilities offered by Spring AOP
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.
[[aop-atconfigurable]]
== Using AspectJ to Dependency Inject Domain Objects with Spring
The Spring container instantiates and configures beans defined in your application
context. It is also possible to ask a bean factory to configure a pre-existing
object, given the name of a bean definition that contains the configuration to be applied.
`spring-aspects.jar` contains an annotation-driven aspect that exploits this
capability to allow dependency injection of any object. The support is intended to
be used for objects created outside of the control of any container. Domain objects
often fall into this category because they are often created programmatically with the
`new` operator or by an ORM tool as a result of a database query.
The `@Configurable` annotation marks a class as being eligible for Spring-driven
configuration. In the simplest case, you can use purely it as a marker annotation, as the
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz.domain;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable
public class Account {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz.domain
import org.springframework.beans.factory.annotation.Configurable
@Configurable
class Account {
// ...
}
----
======
When used as a marker interface in this way, Spring configures new instances of the
annotated type (`Account`, in this case) by using a bean definition (typically
prototype-scoped) with the same name as the fully-qualified type name
(`com.xyz.domain.Account`). Since the default name for a bean is the
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:
[source,xml,indent=0,subs="verbatim"]
----
<bean class="com.xyz.domain.Account" scope="prototype">
<property name="fundsTransferService" ref="fundsTransferService"/>
</bean>
----
If you want to explicitly specify the name of the prototype bean definition to use, you
can do so directly in the annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz.domain;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable("account")
public class Account {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz.domain
import org.springframework.beans.factory.annotation.Configurable
@Configurable("account")
class Account {
// ...
}
----
======
Spring now looks for a bean definition named `account` and uses that as the
definition to configure new `Account` instances.
You can also use autowiring to avoid having to specify a dedicated bean definition at
all. To have Spring apply autowiring, use the `autowire` property of the `@Configurable`
annotation. You can specify either `@Configurable(autowire=Autowire.BY_TYPE)` or
`@Configurable(autowire=Autowire.BY_NAME)` for autowiring by type or by name,
respectively. As an alternative, it is preferable to specify explicit, annotation-driven
dependency injection for your `@Configurable` beans through `@Autowired` or `@Inject`
at the field or method level (see xref:core/beans/annotation-config.adoc[Annotation-based Container Configuration] for further details).
Finally, you can enable Spring dependency checking for the object references in the newly
created and configured object by using the `dependencyCheck` attribute (for example,
`@Configurable(autowire=Autowire.BY_NAME,dependencyCheck=true)`). If this attribute is
set to `true`, Spring validates after configuration that all properties (which
are not primitives or collections) have been set.
Note that using the annotation on its own does nothing. It is the
`AnnotationBeanConfigurerAspect` in `spring-aspects.jar` that acts on the presence of
the annotation. In essence, the aspect says, "after returning from the initialization of
a new object of a type annotated with `@Configurable`, configure the newly created object
using Spring in accordance with the properties of the annotation". In this context,
"initialization" refers to newly instantiated objects (for example, objects instantiated
with the `new` operator) as well as to `Serializable` objects that are undergoing
deserialization (for example, through
https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html[readResolve()]).
[NOTE]
=====
One of the key phrases in the above paragraph is "in essence". For most cases, the
exact semantics of "after returning from the initialization of a new object" are
fine. In this context, "after initialization" means that the dependencies are
injected after the object has been constructed. This means that the dependencies
are not available for use in the constructor bodies of the class. If you want the
dependencies to be injected before the constructor bodies run and thus be
available for use in the body of the constructors, you need to define this on the
`@Configurable` declaration, as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configurable(preConstruction = true)
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configurable(preConstruction = true)
----
======
You can find more information about the language semantics of the various pointcut
types in AspectJ
https://www.eclipse.org/aspectj/doc/next/progguide/semantics-joinPoints.html[in this
appendix] of the https://www.eclipse.org/aspectj/doc/next/progguide/index.html[AspectJ
Programming Guide].
=====
For this to work, the annotated types must be woven with the AspectJ weaver. You can
either use a build-time Ant or Maven task to do this (see, for example, the
https://www.eclipse.org/aspectj/doc/released/devguide/antTasks.html[AspectJ Development
Environment Guide]) or load-time weaving (see xref:core/aop/using-aspectj.adoc#aop-aj-ltw[Load-time Weaving with AspectJ in the Spring Framework]). The
`AnnotationBeanConfigurerAspect` itself needs to be configured by Spring (in order to obtain
a reference to the bean factory that is to be used to configure new objects). If you
use Java-based configuration, you can add `@EnableSpringConfigured` to any
`@Configuration` class, as follows:
[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
object taking place. An example might be a bean in the Spring configuration that creates
domain objects when it is initialized by Spring. In this case, you can use the
`depends-on` bean attribute to manually specify that the bean depends on the
configuration aspect. The following example shows how to use the `depends-on` attribute:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="myService"
class="com.xyz.service.MyService"
depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">
<!-- ... -->
</bean>
----
NOTE: Do not activate `@Configurable` processing through the bean configurer aspect unless you
really mean to rely on its semantics at runtime. In particular, make sure that you do
not use `@Configurable` on bean classes that are registered as regular Spring beans
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
One of the goals of the `@Configurable` support is to enable independent unit testing
of domain objects without the difficulties associated with hard-coded lookups.
If `@Configurable` types have not been woven by AspectJ, the annotation has no affect
during unit testing. You can set mock or stub property references in the object under
test and proceed as normal. If `@Configurable` types have been woven by AspectJ,
you can still unit test outside of the container as normal, but you see a warning
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
The `AnnotationBeanConfigurerAspect` that is used to implement the `@Configurable` support
is an AspectJ singleton aspect. The scope of a singleton aspect is the same as the scope
of `static` members: There is one aspect instance per `ClassLoader` that defines the type.
This means that, if you define multiple application contexts within the same `ClassLoader`
hierarchy, you need to consider where to define the `@EnableSpringConfigured` bean and
where to place `spring-aspects.jar` on the classpath.
Consider a typical Spring web application configuration that has a shared parent application
context that defines common business services, everything needed to support those services,
and one child application context for each servlet (which contains definitions particular
to that servlet). All of these contexts co-exist within the same `ClassLoader` hierarchy,
and so the `AnnotationBeanConfigurerAspect` can hold a reference to only one of them.
In this case, we recommend defining the `@EnableSpringConfigured` bean in the shared
(parent) application context. This defines the services that you are likely to want to
inject into domain objects. A consequence is that you cannot configure domain objects
with references to beans defined in the child (servlet-specific) contexts by using the
@Configurable mechanism (which is probably not something you want to do anyway).
When deploying multiple web applications within the same container, ensure that each
web application loads the types in `spring-aspects.jar` by using its own `ClassLoader`
(for example, by placing `spring-aspects.jar` in `WEB-INF/lib`). If `spring-aspects.jar`
is added only to the container-wide classpath (and hence loaded by the shared parent
`ClassLoader`), all web applications share the same aspect instance (which is probably
not what you want).
[[aop-ajlib-other]]
== Other Spring aspects for AspectJ
In addition to the `@Configurable` aspect, `spring-aspects.jar` contains an AspectJ
aspect that you can use to drive Spring's transaction management for types and methods
annotated with the `@Transactional` annotation. This is primarily intended for users who
want to use the Spring Framework's transaction support outside of the Spring container.
The aspect that interprets `@Transactional` annotations is the
`AnnotationTransactionAspect`. When you use this aspect, you must annotate the
implementation class (or methods within that class or both), not the interface (if
any) that the class implements. AspectJ follows Java's rule that annotations on
interfaces are not inherited.
A `@Transactional` annotation on a class specifies the default transaction semantics for
the execution of any public operation in the class.
A `@Transactional` annotation on a method within the class overrides the default
transaction semantics given by the class annotation (if present). Methods of any
visibility may be annotated, including private methods. Annotating non-public methods
directly is the only way to get transaction demarcation for the execution of such methods.
TIP: Since Spring Framework 4.2, `spring-aspects` provides a similar aspect that offers the
exact same features for the standard `jakarta.transaction.Transactional` annotation. Check
`JtaAnnotationTransactionAspect` for more details.
For AspectJ programmers who want to use the Spring configuration and transaction
management support but do not want to (or cannot) use annotations, `spring-aspects.jar`
also contains `abstract` aspects you can extend to provide your own pointcut
definitions. See the sources for the `AbstractBeanConfigurerAspect` and
`AbstractTransactionAspect` aspects for more information. As an example, the following
excerpt shows how you could write an aspect to configure all instances of objects
defined in the domain model by using prototype bean definitions that match the
fully qualified class names:
[source,java,indent=0,subs="verbatim"]
----
public aspect DomainObjectConfiguration extends AbstractBeanConfigurerAspect {
public DomainObjectConfiguration() {
setBeanWiringInfoResolver(new ClassNameBeanWiringInfoResolver());
}
// the creation of a new bean (any object in the domain model)
protected pointcut beanCreation(Object beanInstance) :
initialization(new(..)) &&
CommonPointcuts.inDomainModel() &&
this(beanInstance);
}
----
[[aop-aj-configure]]
== Configuring AspectJ Aspects by Using Spring IoC
When you use AspectJ aspects with Spring applications, it is natural to both want and
expect to be able to configure such aspects with Spring. The AspectJ runtime itself is
responsible for aspect creation, and the means of configuring the AspectJ-created
aspects through Spring depends on the AspectJ instantiation model (the `per-xxx` clause)
used by the aspect.
The majority of AspectJ aspects are singleton aspects. Configuration of these
aspects is easy. You can create a bean definition that references the aspect type as
normal and include the `factory-method="aspectOf"` bean attribute. This ensures that
Spring obtains the aspect instance by asking AspectJ for it rather than trying to create
an instance itself. The following example shows how to use the `factory-method="aspectOf"` attribute:
[source,xml,indent=0,subs="verbatim"]
----
<bean id="profiler" class="com.xyz.profiler.Profiler"
factory-method="aspectOf"> <1>
<property name="profilingStrategy" ref="jamonProfilingStrategy"/>
</bean>
----
<1> Note the `factory-method="aspectOf"` attribute
Non-singleton aspects are harder to configure. However, it is possible to do so by
creating prototype bean definitions and using the `@Configurable` support from
`spring-aspects.jar` to configure the aspect instances once they have bean created by
the AspectJ runtime.
If you have some @AspectJ aspects that you want to weave with AspectJ (for example,
using load-time weaving for domain model types) and other @AspectJ aspects that you want
to use with Spring AOP, and these aspects are all configured in Spring, you
need to tell the Spring AOP @AspectJ auto-proxying support which exact subset of the
@AspectJ aspects defined in the configuration should be used for auto-proxying. You can
do this by using one or more `<include/>` elements inside the `<aop:aspectj-autoproxy/>`
declaration. Each `<include/>` element specifies a name pattern, and only beans with
names matched by at least one of the patterns are used for Spring AOP auto-proxy
configuration. The following example shows how to use `<include/>` elements:
[source,xml,indent=0,subs="verbatim"]
----
<aop:aspectj-autoproxy>
<aop:include name="thisBean"/>
<aop:include name="thatBean"/>
</aop:aspectj-autoproxy>
----
NOTE: Do not be misled by the name of the `<aop:aspectj-autoproxy/>` element. Using it
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
Load-time weaving (LTW) refers to the process of weaving AspectJ aspects into an
application's class files as they are being loaded into the Java virtual machine (JVM).
The focus of this section is on configuring and using LTW in the specific context of the
Spring Framework. This section is not a general introduction to LTW. For full details on
the specifics of LTW and configuring LTW with only AspectJ (with Spring not being
involved at all), see the
https://www.eclipse.org/aspectj/doc/released/devguide/ltw.html[LTW section of the AspectJ
Development Environment Guide].
The value that the Spring Framework brings to AspectJ LTW is in enabling much
finer-grained control over the weaving process. 'Vanilla' AspectJ LTW is effected by using
a Java (5+) agent, which is switched on by specifying a VM argument when starting up a
JVM. It is, thus, a JVM-wide setting, which may be fine in some situations but is often a
little too coarse. Spring-enabled LTW lets you switch on LTW on a
per-`ClassLoader` basis, which is more fine-grained and which can make more
sense in a 'single-JVM-multiple-application' environment (such as is found in a typical
application server environment).
Further, xref:core/aop/using-aspectj.adoc#aop-aj-ltw-environments[in certain environments], this support enables
load-time weaving without making any modifications to the application server's launch
script that is needed to add `-javaagent:path/to/aspectjweaver.jar` or (as we describe
later in this section) `-javaagent:path/to/spring-instrument.jar`. Developers configure
the application context to enable load-time weaving instead of relying on administrators
who typically are in charge of the deployment configuration, such as the launch script.
Now that the sales pitch is over, let us first walk through a quick example of AspectJ
LTW that uses Spring, followed by detailed specifics about elements introduced in the
example. For a complete example, see the
https://github.com/spring-projects/spring-petclinic[Petclinic sample application].
[[aop-aj-ltw-first-example]]
=== A First Example
Assume that you are an application developer who has been tasked with diagnosing
the cause of some performance problems in a system. Rather than break out a
profiling tool, we are going to switch on a simple profiling aspect that lets us
quickly get some performance metrics. We can then apply a finer-grained profiling
tool to that specific area immediately afterwards.
NOTE: The example presented here uses XML configuration. You can also configure and
use @AspectJ with xref:core/beans/java.adoc[Java configuration]. Specifically, you can use the
`@EnableLoadTimeWeaving` annotation as an alternative to `<context:load-time-weaver/>`
(see xref:core/aop/using-aspectj.adoc#aop-aj-ltw-spring[below] for details).
The following example shows the profiling aspect, which is not fancy.
It is a time-based profiler that uses the @AspectJ-style of aspect declaration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.util.StopWatch;
import org.springframework.core.annotation.Order;
@Aspect
public class ProfilingAspect {
@Around("methodsToBeProfiled()")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
StopWatch sw = new StopWatch(getClass().getSimpleName());
try {
sw.start(pjp.getSignature().getName());
return pjp.proceed();
} finally {
sw.stop();
System.out.println(sw.prettyPrint());
}
}
@Pointcut("execution(public * com.xyz..*.*(..))")
public void methodsToBeProfiled(){}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Pointcut
import org.springframework.util.StopWatch
import org.springframework.core.annotation.Order
@Aspect
class ProfilingAspect {
@Around("methodsToBeProfiled()")
fun profile(pjp: ProceedingJoinPoint): Any {
val sw = StopWatch(javaClass.simpleName)
try {
sw.start(pjp.getSignature().getName())
return pjp.proceed()
} finally {
sw.stop()
println(sw.prettyPrint())
}
}
@Pointcut("execution(public * com.xyz..*.*(..))")
fun methodsToBeProfiled() {
}
}
----
======
We also need to create an `META-INF/aop.xml` file, to inform the AspectJ weaver that
we want to weave our `ProfilingAspect` into our classes. This file convention, namely
the presence of a file (or files) on the Java classpath called `META-INF/aop.xml` is
standard AspectJ. The following example shows the `aop.xml` file:
[source,xml,indent=0,subs="verbatim"]
----
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<!-- only weave classes in our application-specific packages -->
<include within="com.xyz.*"/>
</weaver>
<aspects>
<!-- weave in just this aspect -->
<aspect name="com.xyz.ProfilingAspect"/>
</aspects>
</aspectj>
----
Now we can move on to the Spring-specific portion of the configuration. We need
to configure a `LoadTimeWeaver` (explained later). This load-time weaver is the
essential component responsible for weaving the aspect configuration in one or
more `META-INF/aop.xml` files into the classes in your application. The good
thing is that it does not require a lot of configuration (there are some more
options that you can specify, but these are detailed later), as can be seen in
the following example:
[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">
<!-- a service object; we will be profiling its methods -->
<bean id="entitlementCalculationService"
class="com.xyz.StubEntitlementCalculationService"/>
<!-- this switches on the load-time weaving -->
<context:load-time-weaver/>
</beans>
----
Now that all the required artifacts (the aspect, the `META-INF/aop.xml`
file, and the Spring configuration) are in place, we can create the following
driver class with a `main(..)` method to demonstrate the LTW in action:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz;
// imports
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
EntitlementCalculationService service =
ctx.getBean(EntitlementCalculationService.class);
// the profiling aspect is 'woven' around this method execution
service.calculateEntitlement();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz
// imports
fun main() {
val ctx = ClassPathXmlApplicationContext("beans.xml")
val service = ctx.getBean(EntitlementCalculationService.class)
// the profiling aspect is 'woven' around this method execution
service.calculateEntitlement()
}
----
======
We have one last thing to do. The introduction to this section did say that one could
switch on LTW selectively on a per-`ClassLoader` basis with Spring, and this is true.
However, for this example, we use a Java agent (supplied with Spring) to switch on LTW.
We use the following command to run the `Main` class shown earlier:
[literal,subs="verbatim"]
----
java -javaagent:C:/projects/xyz/lib/spring-instrument.jar com.xyz.Main
----
The `-javaagent` is a flag for specifying and enabling
https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html[agents
to instrument programs that run on the JVM]. The Spring Framework ships with such an
agent, the `InstrumentationSavingAgent`, which is packaged in the
`spring-instrument.jar` that was supplied as the value of the `-javaagent` argument in
the preceding example.
The output from the execution of the `Main` program looks something like the next example.
(I have introduced a `Thread.sleep(..)` statement into the `calculateEntitlement()`
implementation so that the profiler actually captures something other than 0
milliseconds (the `01234` milliseconds is not an overhead introduced by the AOP).
The following listing shows the output we got when we ran our profiler:
[literal,subs="verbatim"]
----
Calculating entitlement
StopWatch 'ProfilingAspect': running time (millis) = 1234
------ ----- ----------------------------
ms % Task name
------ ----- ----------------------------
01234 100% calculateEntitlement
----
Since this LTW is effected by using full-blown AspectJ, we are not limited only to advising
Spring beans. The following slight variation on the `Main` program yields the same
result:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary",chomp="-packages"]
----
package com.xyz;
// imports
public class Main {
public static void main(String[] args) {
new ClassPathXmlApplicationContext("beans.xml");
EntitlementCalculationService service =
new StubEntitlementCalculationService();
// the profiling aspect will be 'woven' around this method execution
service.calculateEntitlement();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
----
package com.xyz
// imports
fun main(args: Array<String>) {
ClassPathXmlApplicationContext("beans.xml")
val service = StubEntitlementCalculationService()
// the profiling aspect will be 'woven' around this method execution
service.calculateEntitlement()
}
----
======
Notice how, in the preceding program, we bootstrap the Spring container and
then create a new instance of the `StubEntitlementCalculationService` totally outside
the context of Spring. The profiling advice still gets woven in.
Admittedly, the example is simplistic. However, the basics of the LTW support in Spring
have all been introduced in the earlier example, and the rest of this section explains
the "why" behind each bit of configuration and usage in detail.
NOTE: The `ProfilingAspect` used in this example may be basic, but it is quite useful. It is a
nice example of a development-time aspect that developers can use during development
and then easily exclude from builds of the application being deployed
into UAT or production.
[[aop-aj-ltw-the-aspects]]
=== Aspects
The aspects that you use in LTW have to be AspectJ aspects. You can write them in
either the AspectJ language itself, or you can write your aspects in the @AspectJ-style.
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'
The AspectJ LTW infrastructure is configured by using one or more `META-INF/aop.xml`
files that are on the Java classpath (either directly or, more typically, in jar files).
The structure and contents of this file is detailed in the LTW part of the
https://www.eclipse.org/aspectj/doc/released/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)
At minimum, you need the following libraries to use the Spring Framework's support
for AspectJ LTW:
* `spring-aop.jar`
* `aspectjweaver.jar`
If you use the xref:core/aop/using-aspectj.adoc#aop-aj-ltw-environments-generic[Spring-provided agent to enable instrumentation]
, you also need:
* `spring-instrument.jar`
[[aop-aj-ltw-spring]]
=== Spring Configuration
The key component in Spring's LTW support is the `LoadTimeWeaver` interface (in the
`org.springframework.instrument.classloading` package), and the numerous implementations
of it that ship with the Spring distribution. A `LoadTimeWeaver` is responsible for
adding one or more `java.lang.instrument.ClassFileTransformers` to a `ClassLoader` at
runtime, which opens the door to all manner of interesting applications, one of which
happens to be the LTW of aspects.
TIP: If you are unfamiliar with the idea of runtime class file transformation, see the
javadoc API documentation for the `java.lang.instrument` package before continuing.
While that documentation is not comprehensive, at least you can see the key interfaces
and classes (for reference as you read through this section).
Configuring a `LoadTimeWeaver` for a particular `ApplicationContext` can be as easy as
adding one line. (Note that you almost certainly need to use an
`ApplicationContext` as your Spring container -- typically, a `BeanFactory` is not
enough because the LTW support uses `BeanFactoryPostProcessors`.)
To enable the Spring Framework's LTW support, you need to configure a `LoadTimeWeaver`,
which typically is done by using the `@EnableLoadTimeWeaving` annotation, as follows:
[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.
The default `LoadTimeWeaver` is the `DefaultContextLoadTimeWeaver` class, which attempts
to decorate an automatically detected `LoadTimeWeaver`. The exact type of `LoadTimeWeaver`
that is "automatically detected" is dependent upon your runtime environment.
The following table summarizes various `LoadTimeWeaver` implementations:
[[aop-aj-ltw-spring-env-impls]]
.DefaultContextLoadTimeWeaver LoadTimeWeavers
|===
| Runtime Environment| `LoadTimeWeaver` implementation
| Running in https://tomcat.apache.org/[Apache Tomcat]
| `TomcatLoadTimeWeaver`
| Running in https://eclipse-ee4j.github.io/glassfish/[GlassFish] (limited to EAR deployments)
| `GlassFishLoadTimeWeaver`
| Running in Red Hat's https://www.jboss.org/jbossas/[JBoss AS] or https://www.wildfly.org/[WildFly]
| `JBossLoadTimeWeaver`
| Running in IBM's https://www-01.ibm.com/software/webservers/appserv/was/[WebSphere]
| `WebSphereLoadTimeWeaver`
| Running in Oracle's
https://www.oracle.com/technetwork/middleware/weblogic/overview/index-085209.html[WebLogic]
| `WebLogicLoadTimeWeaver`
| JVM started with Spring `InstrumentationSavingAgent`
(`java -javaagent:path/to/spring-instrument.jar`)
| `InstrumentationLoadTimeWeaver`
| Fallback, expecting the underlying ClassLoader to follow common conventions
(namely `addTransformer` and optionally a `getThrowawayClassLoader` method)
| `ReflectiveLoadTimeWeaver`
|===
Note that the table lists only the `LoadTimeWeavers` that are autodetected when you
use the `DefaultContextLoadTimeWeaver`. You can specify exactly which `LoadTimeWeaver`
implementation to use.
To specify a specific `LoadTimeWeaver` with Java configuration, implement the
`LoadTimeWeavingConfigurer` interface and override the `getLoadTimeWeaver()` method.
The following example specifies a `ReflectiveLoadTimeWeaver`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
----
@Configuration
@EnableLoadTimeWeaving
public class AppConfig implements LoadTimeWeavingConfigurer {
@Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new ReflectiveLoadTimeWeaver();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
----
@Configuration
@EnableLoadTimeWeaving
class AppConfig : LoadTimeWeavingConfigurer {
override fun getLoadTimeWeaver(): LoadTimeWeaver {
return ReflectiveLoadTimeWeaver()
}
}
----
======
If you use XML-based configuration, you can specify the fully qualified class name
as the value of the `weaver-class` attribute on the `<context:load-time-weaver/>`
element. Again, the following example specifies a `ReflectiveLoadTimeWeaver`:
[source,xml,indent=0,subs="verbatim"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:load-time-weaver
weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
</beans>
----
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`.
Remember that the `LoadTimeWeaver` exists only as a mechanism for Spring's LTW
infrastructure to add one or more `ClassFileTransformers`. The actual
`ClassFileTransformer` that does the LTW is the `ClassPreProcessorAgentAdapter` (from
the `org.aspectj.weaver.loadtime` package) class. See the class-level javadoc of the
`ClassPreProcessorAgentAdapter` class for further details, because the specifics of how
the weaving is actually effected is beyond the scope of this document.
There is one final attribute of the configuration left to discuss: the `aspectjWeaving`
attribute (or `aspectj-weaving` if you use XML). This attribute controls whether LTW
is enabled or not. It accepts one of three possible values, with the default value being
`autodetect` if the attribute is not present. The following table summarizes the three
possible values:
[[aop-aj-ltw-ltw-tag-attrs]]
.AspectJ weaving attribute values
|===
| Annotation Value| XML Value| Explanation
| `ENABLED`
| `on`
| AspectJ weaving is on, and aspects are woven at load-time as appropriate.
| `DISABLED`
| `off`
| LTW is off. No aspect is woven at load-time.
| `AUTODETECT`
| `autodetect`
| If the Spring LTW infrastructure can find at least one `META-INF/aop.xml` file,
then AspectJ weaving is on. Otherwise, it is off. This is the default value.
|===
[[aop-aj-ltw-environments]]
=== Environment-specific Configuration
This last section contains any additional settings and configuration that you need
when you use Spring's LTW support in environments such as application servers and web
containers.
[[aop-aj-ltw-environments-tomcat-jboss-etc]]
==== Tomcat, JBoss, WebSphere, WebLogic
Tomcat, JBoss/WildFly, IBM WebSphere Application Server and Oracle WebLogic Server all
provide a general app `ClassLoader` that is capable of local instrumentation. Spring's
native LTW may leverage those ClassLoader implementations to provide AspectJ weaving.
You can simply enable load-time weaving, as xref:core/aop/using-aspectj.adoc[described earlier].
Specifically, you do not need to modify the JVM launch script to add
`-javaagent:path/to/spring-instrument.jar`.
Note that on JBoss, you may need to disable the app server scanning to prevent it from
loading the classes before the application actually starts. A quick workaround is to add
to your artifact a file named `WEB-INF/jboss-scanning.xml` with the following content:
[source,xml,indent=0,subs="verbatim"]
----
<scanning xmlns="urn:jboss:scanning:1.0"/>
----
[[aop-aj-ltw-environments-generic]]
==== Generic Java Applications
When class instrumentation is required in environments that are not supported by
specific `LoadTimeWeaver` implementations, a JVM agent is the general solution.
For such cases, Spring provides `InstrumentationLoadTimeWeaver` which requires a
Spring-specific (but very general) JVM agent, `spring-instrument.jar`, autodetected
by common `@EnableLoadTimeWeaving` and `<context:load-time-weaver/>` setups.
To use it, you must start the virtual machine with the Spring agent by supplying
the following JVM options:
[literal]
[subs="verbatim"]
----
-javaagent:/path/to/spring-instrument.jar
----
Note that this requires modification of the JVM launch script, which may prevent you
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.
@@ -1,11 +1,11 @@
[[core.aot]]
[[aot]]
= Ahead of Time Optimizations
This chapter covers Spring's Ahead of Time (AOT) optimizations.
For AOT support specific to integration tests, see <<testing.adoc#testcontext-aot, Ahead of Time Support for Tests>>.
For AOT support specific to integration tests, see xref:testing/testcontext-framework/aot.adoc[Ahead of Time Support for Tests].
[[core.aot.introduction]]
[[aot.introduction]]
== Introduction to Ahead of Time Optimizations
Spring's support for AOT optimizations is meant to inspect an `ApplicationContext` at build time and apply decisions and discovery logic that usually happens at runtime.
@@ -16,7 +16,11 @@ Applying such optimizations early implies the following restrictions:
* The classpath is fixed and fully defined at build time.
* The beans defined in your application cannot change at runtime, meaning:
** `@Profile`, in particular profile-specific configuration needs to be chosen at build time.
** Environment properties that impact the presence of a bean (`@Conditional`) are only considered at build time.
** `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 (see related https://github.com/spring-projects/spring-framework/issues/29555[spring-framework#29555] issue).
* Make sure that the bean type is as precise as possible.
TIP: See also the xref:core/aot.adoc#aot.bestpractices[] section.
When these restrictions are in place, it becomes possible to perform ahead-of-time processing at build time and generate additional assets.
A Spring AOT processed application typically generates:
@@ -28,7 +32,7 @@ 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.
[[core.aot.basics]]
[[aot.basics]]
== AOT engine overview
The entry point of the AOT engine for processing an `ApplicationContext` arrangement is `ApplicationContextAotGenerator`. It takes care of the following steps, based on a `GenericApplicationContext` that represents the application to optimize and a {api-spring-framework}/aot/generate/GenerationContext.html[`GenerationContext`]:
@@ -44,7 +48,7 @@ The `RuntimeHints` instance can also be used to generate the relevant GraalVM na
Those steps are covered in greater detail in the sections below.
[[core.aot.refresh]]
[[aot.refresh]]
== Refresh for AOT Processing
Refresh for AOT processing is supported on all `GenericApplicationContext` implementations.
@@ -52,15 +56,15 @@ An application context is created with any number of entry points, usually in th
Let's look at a basic example:
include::code:AotProcessingSample[tag=myapplication]
include-code::./AotProcessingSample[tag=myapplication]
Starting this application with the regular runtime involves a number of steps including classpath scanning, configuration class parsing, bean instantiation, and lifecycle callback handling.
Refresh for AOT processing only applies a subset of what happens with a <<beans-introduction,regular `refresh`>>.
Refresh for AOT processing only applies a subset of what happens with a xref:core/beans/introduction.adoc[regular `refresh`].
AOT processing can be triggered as follows:
include::code:AotProcessingSample[tag=aotcontext]
include-code::./AotProcessingSample[tag=aotcontext]
In this mode, <<beans-factory-extension-factory-postprocessors,`BeanFactoryPostProcessor` implementations>> are invoked as usual.
In this mode, xref:core/beans/factory-extension.adoc#beans-factory-extension-factory-postprocessors[`BeanFactoryPostProcessor` implementations] are invoked as usual.
This includes configuration class parsing, import selectors, classpath scanning, etc.
Such steps make sure that the `BeanRegistry` contains the relevant bean definitions for the application.
If bean definitions are guarded by conditions (such as `@Profile`), these are discarded at this stage.
@@ -72,9 +76,9 @@ These are:
* `SmartInstantiationAwareBeanPostProcessor` implementations determine a more precise bean type if necessary.
This makes sure to create any proxy that will be required at runtime.
One 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.
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.
[[core.aot.bean-factory-initialization-contributions]]
[[aot.bean-factory-initialization-contributions]]
== Bean Factory Initialization AOT Contributions
Components that want to participate in this step can implement the {api-spring-framework}/beans/factory/aot/BeanFactoryInitializationAotProcessor.html[`BeanFactoryInitializationAotProcessor`] interface.
@@ -97,7 +101,7 @@ If such a bean is registered using an `@Bean` factory method, ensure the method
====
[[core.aot.bean-registration-contributions]]
[[aot.bean-registration-contributions]]
=== Bean Registration AOT Contributions
A core `BeanFactoryInitializationAotProcessor` implementation is responsible for collecting the necessary contributions for each candidate `BeanDefinition`.
@@ -106,7 +110,7 @@ It does so using a dedicated `BeanRegistrationAotProcessor`.
This interface is used as follows:
* Implemented by a `BeanPostProcessor` bean, to replace its runtime behavior.
For instance <<beans-factory-extension-bpp-examples-aabpp,`AutowiredAnnotationBeanPostProcessor`>> implements this interface to generate code that injects members annotated with `@Autowired`.
For instance xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp-examples-aabpp[`AutowiredAnnotationBeanPostProcessor`] implements this interface to generate code that injects members annotated with `@Autowired`.
* Implemented by a type registered in `META-INF/spring/aot.factories` with a key equal to the fully qualified name of the interface.
Typically used when the bean definition needs to be tuned for specific features of the core framework.
@@ -122,8 +126,11 @@ This is the default behavior, since tuning the generated code for a bean definit
Taking our previous example, let's assume that `DataSourceConfiguration` is as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration(proxyBeanMethods = false)
public class DataSourceConfiguration {
@@ -135,12 +142,16 @@ Taking our previous example, let's assume that `DataSourceConfiguration` is as f
}
----
======
Since there isn't any particular condition on this class, `dataSourceConfiguration` and `dataSource` are identified as candidates.
The AOT engine will convert the configuration class above to code similar to the following:
[tabs]
======
Java::
+
[source,java,indent=0,role="primary"]
.Java
----
/**
* Bean definitions for {@link DataSourceConfiguration}
@@ -175,6 +186,7 @@ The AOT engine will convert the configuration class above to code similar to the
}
}
----
======
NOTE: The exact code generated may differ depending on the exact nature of your bean definitions.
@@ -183,8 +195,176 @@ There is a bean definition for `dataSourceConfiguration` and one for `dataSource
When a `datasource` instance is required, a `BeanInstanceSupplier` is called.
This supplier invokes the `dataSource()` method on the `dataSourceConfiguration` bean.
[[aot.bestpractices]]
== Best Practices
[[core.aot.hints]]
The AOT engine is designed to handle as many use cases as possible, with no code change in applications.
However, keep in mind that some optimizations are made at build time based on a static definition of the beans.
This section lists the best practices that make sure your application is ready for AOT.
[[aot.bestpractices.bean-type]]
=== Expose The Most Precise Bean Type
While your application may interact with an interface that a bean implements, it is still very important to declare the most precise type.
The AOT engine performs additional checks on the bean type, such as detecting the presence of `@Autowired` members, or lifecycle callback methods.
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",role="primary"]
----
@Configuration(proxyBeanMethods = false)
public class UserConfiguration {
@Bean
public MyInterface myInterface() {
return new MyImplementation();
}
}
----
======
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 be rewritten as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration(proxyBeanMethods = false)
public class UserConfiguration {
@Bean
public MyImplementation myInterface() {
return new MyImplementation();
}
}
----
======
If you are registering bean definitions programmatically, consider using `RootBeanBefinition` as it allows to specify a `ResolvableType` that handles generics.
[[aot.bestpractices.factory-bean]]
=== 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 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:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class ClientFactoryBean<T extends AbstractClient> implements FactoryBean<T> {
}
----
======
A concrete client declaration should provide a resolved generic for the client, as shown in the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration(proxyBeanMethods = false)
public class UserConfiguration {
@Bean
public ClientFactoryBean<MyClient> myClient() {
return new ClientFactoryBean<>(...);
}
}
----
======
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.
3. Set the `ResolvableType` to a resolved generic, which makes sure the most precise type is exposed.
The following example showcases a basic definition:
[tabs]
======
Java::
+
[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);
----
======
[[aot.bestpractices.jpa]]
=== JPA
The JPA persistence unit has to be known upfront for certain optimizations to apply. Consider the following basic example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Bean
LocalContainerEntityManagerFactoryBean customDBEntityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setPackagesToScan("com.example.app");
return factoryBean;
}
----
======
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",role="primary"]
----
@Bean
PersistenceManagedTypes persistenceManagedTypes(ResourceLoader resourceLoader) {
return new PersistenceManagedTypesScanner(resourceLoader)
.scan("com.example.app");
}
@Bean
LocalContainerEntityManagerFactoryBean customDBEntityManagerFactory(DataSource dataSource, PersistenceManagedTypes managedTypes) {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setManagedTypes(managedTypes);
return factoryBean;
}
----
======
[[aot.hints]]
== Runtime Hints
Running an application as a native image requires additional information compared to a regular JVM runtime.
@@ -195,11 +375,15 @@ Consequently, if the application needs to load a resource, it must be referenced
The {api-spring-framework}/aot/hint/RuntimeHints.html[`RuntimeHints`] API collects the need for reflection, resource loading, serialization, and JDK proxies at runtime.
The following example makes sure that `config/app.properties` can be loaded from the classpath at runtime within a native image:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
runtimeHints.resources().registerPattern("config/app.properties");
----
======
A number of contracts are handled automatically during AOT processing.
For instance, the return type of a `@Controller` method is inspected, and relevant reflection hints are added if Spring detects that the type should be serialized (typically to JSON).
@@ -208,14 +392,14 @@ For cases that the core container cannot infer, you can register such hints prog
A number of convenient annotations are also provided for common use cases.
[[core.aot.hints.import-runtime-hints]]
[[aot.hints.import-runtime-hints]]
=== `@ImportRuntimeHints`
`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[]
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 won't be contributed either.
@@ -223,7 +407,7 @@ This way, if the component is not contributed to the `BeanFactory`, the hints wo
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.
[[core.aot.hints.reflective]]
[[aot.hints.reflective]]
=== `@Reflective`
{api-spring-framework}/aot/hint/annotation/Reflective.html[`@Reflective`] provides an idiomatic way to flag the need for reflection on an annotated element.
@@ -237,7 +421,7 @@ Library authors can reuse this annotation for their own purposes.
If components other than Spring beans need to be processed, a `BeanFactoryInitializationAotProcessor` can detect the relevant types and use `ReflectiveRuntimeHintsRegistrar` to process them.
[[core.aot.hints.register-reflection-for-binding]]
[[aot.hints.register-reflection-for-binding]]
=== `@RegisterReflectionForBinding`
{api-spring-framework}/aot/hint/annotation/RegisterReflectionForBinding.html[`@RegisterReflectionForBinding`] is a specialization of `@Reflective` that registers the need for serializing arbitrary types.
@@ -246,8 +430,11 @@ A typical use case is the use of DTOs that the container cannot infer, such as u
`@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",role="primary"]
.Java
----
@Component
public class OrderService {
@@ -259,15 +446,16 @@ The following example registers `Account` for serialization.
}
----
======
[[core.aot.hints.testing]]
[[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` 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]
include-code::./SpellCheckServiceTests[tag=hintspredicates]
With `RuntimeHintsPredicates`, we can check for reflection, resource, serialization, or proxy generation hints.
This approach works well for unit tests but implies that the runtime behavior of a component is well known.
@@ -279,11 +467,11 @@ For more targeted discovery and testing, Spring Framework ships a dedicated modu
This module contains the RuntimeHints Agent, a Java agent that records all method invocations that are related to runtime hints and helps you to assert that a given `RuntimeHints` instance covers all recorded invocations.
Let's consider a piece of infrastructure for which we'd like to test the hints we're contributing during the AOT processing phase.
include::code:SampleReflection[]
include-code::./SampleReflection[]
We can then write a unit test (no native compilation required) that checks our contributed hints:
include::code:SampleReflectionRuntimeHintsTests[]
include-code::./SampleReflectionRuntimeHintsTests[]
If you forgot to contribute a hint, the test will fail and provide some details about the invocation:
@@ -0,0 +1,7 @@
[[appendix]]
= Appendix
:page-section-summary-toc: 1
@@ -0,0 +1,57 @@
[[application-startup-steps]]
= Application Startup Steps
This part of the appendix lists the existing `StartupSteps` that the core container is instrumented with.
WARNING: The name and detailed information about each startup step is not part of the public contract and
is subject to change; this is considered as an implementation detail of the core container and will follow
its behavior changes.
.Application startup steps defined in the core container
|===
| Name| Description| Tags
| `spring.beans.instantiate`
| Instantiation of a bean and its dependencies.
| `beanName` the name of the bean, `beanType` the type required at the injection point.
| `spring.beans.smart-initialize`
| Initialization of `SmartInitializingSingleton` beans.
| `beanName` the name of the bean.
| `spring.context.annotated-bean-reader.create`
| Creation of the `AnnotatedBeanDefinitionReader`.
|
| `spring.context.base-packages.scan`
| Scanning of base packages.
| `packages` array of base packages for scanning.
| `spring.context.beans.post-process`
| Beans post-processing phase.
|
| `spring.context.bean-factory.post-process`
| Invocation of the `BeanFactoryPostProcessor` beans.
| `postProcessor` the current post-processor.
| `spring.context.beandef-registry.post-process`
| Invocation of the `BeanDefinitionRegistryPostProcessor` beans.
| `postProcessor` the current post-processor.
| `spring.context.component-classes.register`
| Registration of component classes through `AnnotationConfigApplicationContext#register`.
| `classes` array of given classes for registration.
| `spring.context.config-classes.enhance`
| Enhancement of configuration classes with CGLIB proxies.
| `classCount` count of enhanced classes.
| `spring.context.config-classes.parse`
| Configuration classes parsing phase with the `ConfigurationClassPostProcessor`.
| `classCount` count of processed classes.
| `spring.context.refresh`
| Application context refresh phase.
|
|===
@@ -0,0 +1,661 @@
[[xsd-schemas]]
= XML Schemas
This part of the appendix lists XML schemas related to the core container.
[[xsd-schemas-util]]
== The `util` Schema
As the name implies, the `util` tags deal with common, utility configuration
issues, such as configuring collections, referencing constants, and so forth.
To use the tags in the `util` schema, you need to have the following preamble at the top
of your Spring XML configuration file (the text in the snippet references the
correct schema so that the tags in the `util` namespace are available to you):
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
<!-- bean definitions here -->
</beans>
----
[[xsd-schemas-util-constant]]
=== Using `<util:constant/>`
Consider the following bean definition:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="..." class="...">
<property name="isolation">
<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />
</property>
</bean>
----
The preceding configuration uses a Spring `FactoryBean` implementation (the
`FieldRetrievingFactoryBean`) to set the value of the `isolation` property on a bean
to the value of the `java.sql.Connection.TRANSACTION_SERIALIZABLE` constant. This is
all well and good, but it is verbose and (unnecessarily) exposes Spring's internal
plumbing to the end user.
The following XML Schema-based version is more concise, clearly expresses the
developer's intent ("`inject this constant value`"), and it reads better:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="..." class="...">
<property name="isolation">
<util:constant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
</property>
</bean>
----
[[xsd-schemas-util-frfb]]
==== Setting a Bean Property or Constructor Argument from a Field Value
{api-spring-framework}/beans/factory/config/FieldRetrievingFactoryBean.html[`FieldRetrievingFactoryBean`]
is a `FactoryBean` that retrieves a `static` or non-static field value. It is typically
used for retrieving `public` `static` `final` constants, which may then be used to set a
property value or constructor argument for another bean.
The following example shows how a `static` field is exposed, by using the
{api-spring-framework}/beans/factory/config/FieldRetrievingFactoryBean.html#setStaticField(java.lang.String)[`staticField`]
property:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="myField"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="staticField" value="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
</bean>
----
There is also a convenience usage form where the `static` field is specified as the bean
name, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
----
This does mean that there is no longer any choice in what the bean `id` is (so any other
bean that refers to it also has to use this longer name), but this form is very
concise to define and very convenient to use as an inner bean since the `id` does not have
to be specified for the bean reference, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="..." class="...">
<property name="isolation">
<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />
</property>
</bean>
----
You can also access a non-static (instance) field of another bean, as
described in the API documentation for the
{api-spring-framework}/beans/factory/config/FieldRetrievingFactoryBean.html[`FieldRetrievingFactoryBean`]
class.
Injecting enumeration values into beans as either property or constructor arguments is
easy to do in Spring. You do not actually have to do anything or know anything about
the Spring internals (or even about classes such as the `FieldRetrievingFactoryBean`).
The following example enumeration shows how easy injecting an enum value is:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package jakarta.persistence;
public enum PersistenceContextType {
TRANSACTION,
EXTENDED
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package jakarta.persistence
enum class PersistenceContextType {
TRANSACTION,
EXTENDED
}
----
======
Now consider the following setter of type `PersistenceContextType` and the corresponding bean definition:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package example;
public class Client {
private PersistenceContextType persistenceContextType;
public void setPersistenceContextType(PersistenceContextType type) {
this.persistenceContextType = type;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package example
class Client {
lateinit var persistenceContextType: PersistenceContextType
}
----
======
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean class="example.Client">
<property name="persistenceContextType" value="TRANSACTION"/>
</bean>
----
[[xsd-schemas-util-property-path]]
=== Using `<util:property-path/>`
Consider the following example:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- target bean to be referenced by name -->
<bean id="testBean" class="org.springframework.beans.TestBean" scope="prototype">
<property name="age" value="10"/>
<property name="spouse">
<bean class="org.springframework.beans.TestBean">
<property name="age" value="11"/>
</bean>
</property>
</bean>
<!-- results in 10, which is the value of property 'age' of bean 'testBean' -->
<bean id="testBean.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
----
The preceding configuration uses a Spring `FactoryBean` implementation (the
`PropertyPathFactoryBean`) to create a bean (of type `int`) called `testBean.age` that
has a value equal to the `age` property of the `testBean` bean.
Now consider the following example, which adds a `<util:property-path/>` element:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- target bean to be referenced by name -->
<bean id="testBean" class="org.springframework.beans.TestBean" scope="prototype">
<property name="age" value="10"/>
<property name="spouse">
<bean class="org.springframework.beans.TestBean">
<property name="age" value="11"/>
</bean>
</property>
</bean>
<!-- results in 10, which is the value of property 'age' of bean 'testBean' -->
<util:property-path id="name" path="testBean.age"/>
----
The value of the `path` attribute of the `<property-path/>` element follows the form of
`beanName.beanProperty`. In this case, it picks up the `age` property of the bean named
`testBean`. The value of that `age` property is `10`.
[[xsd-schemas-util-property-path-dependency]]
==== Using `<util:property-path/>` to Set a Bean Property or Constructor Argument
`PropertyPathFactoryBean` is a `FactoryBean` that evaluates a property path on a given
target object. The target object can be specified directly or by a bean name. You can then use this
value in another bean definition as a property value or constructor
argument.
The following example shows a path being used against another bean, by name:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- target bean to be referenced by name -->
<bean id="person" class="org.springframework.beans.TestBean" scope="prototype">
<property name="age" value="10"/>
<property name="spouse">
<bean class="org.springframework.beans.TestBean">
<property name="age" value="11"/>
</bean>
</property>
</bean>
<!-- results in 11, which is the value of property 'spouse.age' of bean 'person' -->
<bean id="theAge"
class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
<property name="targetBeanName" value="person"/>
<property name="propertyPath" value="spouse.age"/>
</bean>
----
In the following example, a path is evaluated against an inner bean:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- results in 12, which is the value of property 'age' of the inner bean -->
<bean id="theAge"
class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
<property name="targetObject">
<bean class="org.springframework.beans.TestBean">
<property name="age" value="12"/>
</bean>
</property>
<property name="propertyPath" value="age"/>
</bean>
----
There is also a shortcut form, where the bean name is the property path.
The following example shows the shortcut form:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- results in 10, which is the value of property 'age' of bean 'person' -->
<bean id="person.age"
class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
----
This form does mean that there is no choice in the name of the bean. Any reference to it
also has to use the same `id`, which is the path. If used as an inner
bean, there is no need to refer to it at all, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="..." class="...">
<property name="age">
<bean id="person.age"
class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
</property>
</bean>
----
You can specifically set the result type in the actual definition. This is not necessary
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/>`
Consider the following example:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.Properties instance with values loaded from the supplied location -->
<bean id="jdbcConfiguration" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:com/foo/jdbc-production.properties"/>
</bean>
----
The preceding configuration uses a Spring `FactoryBean` implementation (the
`PropertiesFactoryBean`) to instantiate a `java.util.Properties` instance with values
loaded from the supplied xref:web/webflux-webclient/client-builder.adoc#webflux-client-builder-reactor-resources[`Resource`] location).
The following example uses a `util:properties` element to make a more concise representation:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.Properties instance with values loaded from the supplied location -->
<util:properties id="jdbcConfiguration" location="classpath:com/foo/jdbc-production.properties"/>
----
[[xsd-schemas-util-list]]
=== Using `<util:list/>`
Consider the following example:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.List instance with values loaded from the supplied 'sourceList' -->
<bean id="emails" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list>
<value>pechorin@hero.org</value>
<value>raskolnikov@slums.org</value>
<value>stavrogin@gov.org</value>
<value>porfiry@gov.org</value>
</list>
</property>
</bean>
----
The preceding configuration uses a Spring `FactoryBean` implementation (the
`ListFactoryBean`) to create a `java.util.List` instance and initialize it with values taken
from the supplied `sourceList`.
The following example uses a `<util:list/>` element to make a more concise representation:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.List instance with the supplied values -->
<util:list id="emails">
<value>pechorin@hero.org</value>
<value>raskolnikov@slums.org</value>
<value>stavrogin@gov.org</value>
<value>porfiry@gov.org</value>
</util:list>
----
You can also explicitly control the exact type of `List` that is instantiated and
populated by using the `list-class` attribute on the `<util:list/>` element. For
example, if we really need a `java.util.LinkedList` to be instantiated, we could use the
following configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<util:list id="emails" list-class="java.util.LinkedList">
<value>jackshaftoe@vagabond.org</value>
<value>eliza@thinkingmanscrumpet.org</value>
<value>vanhoek@pirate.org</value>
<value>d'Arcachon@nemesis.org</value>
</util:list>
----
If no `list-class` attribute is supplied, the container chooses a `List` implementation.
[[xsd-schemas-util-map]]
=== Using `<util:map/>`
Consider the following example:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.Map instance with values loaded from the supplied 'sourceMap' -->
<bean id="emails" class="org.springframework.beans.factory.config.MapFactoryBean">
<property name="sourceMap">
<map>
<entry key="pechorin" value="pechorin@hero.org"/>
<entry key="raskolnikov" value="raskolnikov@slums.org"/>
<entry key="stavrogin" value="stavrogin@gov.org"/>
<entry key="porfiry" value="porfiry@gov.org"/>
</map>
</property>
</bean>
----
The preceding configuration uses a Spring `FactoryBean` implementation (the
`MapFactoryBean`) to create a `java.util.Map` instance initialized with key-value pairs
taken from the supplied `'sourceMap'`.
The following example uses a `<util:map/>` element to make a more concise representation:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.Map instance with the supplied key-value pairs -->
<util:map id="emails">
<entry key="pechorin" value="pechorin@hero.org"/>
<entry key="raskolnikov" value="raskolnikov@slums.org"/>
<entry key="stavrogin" value="stavrogin@gov.org"/>
<entry key="porfiry" value="porfiry@gov.org"/>
</util:map>
----
You can also explicitly control the exact type of `Map` that is instantiated and
populated by using the `'map-class'` attribute on the `<util:map/>` element. For
example, if we really need a `java.util.TreeMap` to be instantiated, we could use the
following configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<util:map id="emails" map-class="java.util.TreeMap">
<entry key="pechorin" value="pechorin@hero.org"/>
<entry key="raskolnikov" value="raskolnikov@slums.org"/>
<entry key="stavrogin" value="stavrogin@gov.org"/>
<entry key="porfiry" value="porfiry@gov.org"/>
</util:map>
----
If no `'map-class'` attribute is supplied, the container chooses a `Map` implementation.
[[xsd-schemas-util-set]]
=== Using `<util:set/>`
Consider the following example:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.Set instance with values loaded from the supplied 'sourceSet' -->
<bean id="emails" class="org.springframework.beans.factory.config.SetFactoryBean">
<property name="sourceSet">
<set>
<value>pechorin@hero.org</value>
<value>raskolnikov@slums.org</value>
<value>stavrogin@gov.org</value>
<value>porfiry@gov.org</value>
</set>
</property>
</bean>
----
The preceding configuration uses a Spring `FactoryBean` implementation (the
`SetFactoryBean`) to create a `java.util.Set` instance initialized with values taken
from the supplied `sourceSet`.
The following example uses a `<util:set/>` element to make a more concise representation:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- creates a java.util.Set instance with the supplied values -->
<util:set id="emails">
<value>pechorin@hero.org</value>
<value>raskolnikov@slums.org</value>
<value>stavrogin@gov.org</value>
<value>porfiry@gov.org</value>
</util:set>
----
You can also explicitly control the exact type of `Set` that is instantiated and
populated by using the `set-class` attribute on the `<util:set/>` element. For
example, if we really need a `java.util.TreeSet` to be instantiated, we could use the
following configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<util:set id="emails" set-class="java.util.TreeSet">
<value>pechorin@hero.org</value>
<value>raskolnikov@slums.org</value>
<value>stavrogin@gov.org</value>
<value>porfiry@gov.org</value>
</util:set>
----
If no `set-class` attribute is supplied, the container chooses a `Set` implementation.
[[xsd-schemas-aop]]
== The `aop` Schema
The `aop` tags deal with configuring all things AOP in Spring, including Spring's
own proxy-based AOP framework and Spring's integration with the AspectJ AOP framework.
These tags are comprehensively covered in the chapter entitled xref:core/aop.adoc[Aspect Oriented Programming with Spring]
.
In the interest of completeness, to use the tags in the `aop` schema, you need to have
the following preamble at the top of your Spring XML configuration file (the text in the
snippet references the correct schema so that the tags in the `aop` namespace
are available to you):
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean definitions here -->
</beans>
----
[[xsd-schemas-context]]
== The `context` Schema
The `context` tags deal with `ApplicationContext` configuration that relates to plumbing
-- that is, not usually beans that are important to an end-user but rather beans that do
a lot of the "`grunt`" work in Spring, such as `BeanfactoryPostProcessors`. The following
snippet references the correct schema so that the elements in the `context` namespace are
available to you:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- bean definitions here -->
</beans>
----
[[xsd-schemas-context-pphc]]
=== Using `<property-placeholder/>`
This element activates the replacement of `${...}` placeholders, which are resolved against a
specified properties file (as a xref:web/webflux-webclient/client-builder.adoc#webflux-client-builder-reactor-resources[Spring resource location]). This element
is a convenience mechanism that sets up a xref:core/beans/factory-extension.adoc#beans-factory-placeholderconfigurer[`PropertySourcesPlaceholderConfigurer`]
for you. If you need more control over the specific
`PropertySourcesPlaceholderConfigurer` setup, you can explicitly define it as a bean yourself.
[[xsd-schemas-context-ac]]
=== Using `<annotation-config/>`
This element activates the Spring infrastructure to detect annotations in bean classes:
* Spring's xref:core/beans/basics.adoc#beans-factory-metadata[`@Configuration`] model
* xref:core/beans/annotation-config.adoc[`@Autowired`/`@Inject`], `@Value`, and `@Lookup`
* JSR-250's `@Resource`, `@PostConstruct`, and `@PreDestroy` (if available)
* JAX-WS's `@WebServiceRef` and EJB 3's `@EJB` (if available)
* JPA's `@PersistenceContext` and `@PersistenceUnit` (if available)
* Spring's xref:core/beans/context-introduction.adoc#context-functionality-events-annotation[`@EventListener`]
Alternatively, you can choose to explicitly activate the individual `BeanPostProcessors`
for those annotations.
NOTE: This element does not activate processing of Spring's
xref:data-access/transaction/declarative/annotations.adoc[`@Transactional`] annotation;
you can use the <<data-access.adoc#tx-decl-explained, `<tx:annotation-driven/>`>>
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]
.
[[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]
.
[[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]
.
[[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]
.
[[xsd-schemas-beans]]
== The Beans Schema
Last but not least, we have the elements in the `beans` schema. These elements
have been in Spring since the very dawn of the framework. Examples of the various elements
in the `beans` schema are not shown here because they are quite comprehensively covered
in xref:core/beans/dependencies/factory-properties-detailed.adoc[dependencies and configuration in detail]
(and, indeed, in that entire xref:web/webmvc-view/mvc-xslt.adoc#mvc-view-xslt-beandefs[chapter]).
Note that you can add zero or more key-value pairs to `<bean/>` XML definitions.
What, if anything, is done with this extra metadata is totally up to your own custom
logic (and so is typically only of use if you write your own custom elements as described
in the appendix entitled xref:core/appendix/xml-custom.adoc[XML Schema Authoring]).
The following example shows the `<meta/>` element in the context of a surrounding `<bean/>`
(note that, without any logic to interpret it, the metadata is effectively useless
as it stands).
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="foo" class="x.y.Foo">
<meta key="cacheName" value="foo"/> <1>
<property name="name" value="Rick"/>
</bean>
</beans>
----
<1> This is the example `meta` element
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.
@@ -0,0 +1,9 @@
[[beans]]
= The IoC Container
:page-section-summary-toc: 1
This chapter covers Spring's Inversion of Control (IoC) container.
@@ -0,0 +1,81 @@
[[beans-annotation-config]]
= Annotation-based Container Configuration
.Are annotations better than XML for configuring Spring?
****
The introduction of annotation-based configuration raised the question of whether this
approach is "`better`" than XML. The short answer is "`it depends.`" The long answer is
that each approach has its pros and cons, and, usually, it is up to the developer to
decide which strategy suits them better. Due to the way they are defined, annotations
provide a lot of context in their declaration, leading to shorter and more concise
configuration. However, XML excels at wiring up components without touching their source
code or recompiling them. Some developers prefer having the wiring close to the source
while others argue that annotated classes are no longer POJOs and, furthermore, that the
configuration becomes decentralized and harder to control.
No matter the choice, Spring can accommodate both styles and even mix them together.
It is worth pointing out that through its xref:core/beans/java.adoc[JavaConfig] option, Spring lets
annotations be used in a non-invasive way, without touching the target components'
source code and that, in terms of tooling, all configuration styles are supported by
https://spring.io/tools[Spring Tools] for Eclipse, Visual Studio Code, and Theia.
****
An alternative to XML setup is provided by annotation-based configuration, which relies
on bytecode metadata for wiring up components instead of XML declarations. Instead of
using XML to describe a bean wiring, the developer moves the configuration into the
component class itself by using annotations on the relevant class, method, or field
declaration. As mentioned in xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp-examples-aabpp[Example: The `AutowiredAnnotationBeanPostProcessor`], using a
`BeanPostProcessor` in conjunction with annotations is a common means of extending the
Spring IoC container. For example, the xref:core/beans/annotation-config/autowired.adoc[`@Autowired`]
annotation provides the same capabilities as described in xref:core/beans/dependencies/factory-autowire.adoc[Autowiring Collaborators] but
with more fine-grained control and wider applicability. In addition, Spring provides
support for JSR-250 annotations, such as `@PostConstruct` and `@PreDestroy`, as well as
support for JSR-330 (Dependency Injection for Java) annotations contained in the
`jakarta.inject` package such as `@Inject` and `@Named`. Details about those annotations
can be found in the xref:core/beans/standard-annotations.adoc[relevant section].
[NOTE]
====
Annotation injection is performed before XML injection. Thus, the XML configuration
overrides the annotations for properties wired through both approaches.
====
As always, you can register the post-processors as individual bean definitions, but they
can also be implicitly registered by including the following tag in an XML-based Spring
configuration (notice the inclusion of the `context` namespace):
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
----
The `<context:annotation-config/>` element implicitly registers the following post-processors:
* {api-spring-framework}/context/annotation/ConfigurationClassPostProcessor.html[`ConfigurationClassPostProcessor`]
* {api-spring-framework}/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.html[`AutowiredAnnotationBeanPostProcessor`]
* {api-spring-framework}/context/annotation/CommonAnnotationBeanPostProcessor.html[`CommonAnnotationBeanPostProcessor`]
* {api-spring-framework}/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.html[`PersistenceAnnotationBeanPostProcessor`]
* {api-spring-framework}/context/event/EventListenerMethodProcessor.html[`EventListenerMethodProcessor`]
[NOTE]
====
`<context:annotation-config/>` only looks for annotations on beans in the same
application context in which it is defined. This means that, if you put
`<context:annotation-config/>` in a `WebApplicationContext` for a `DispatcherServlet`,
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.
====
@@ -0,0 +1,114 @@
[[beans-autowired-annotation-primary]]
= 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
`@Primary` annotation. `@Primary` indicates that a particular bean should be given
preference when multiple beans are candidates to be autowired to a single-valued
dependency. If exactly one primary bean exists among the candidates, it becomes the
autowired value.
Consider the following configuration that defines `firstMovieCatalog` as the
primary `MovieCatalog`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class MovieConfiguration {
@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class MovieConfiguration {
@Bean
@Primary
fun firstMovieCatalog(): MovieCatalog { ... }
@Bean
fun secondMovieCatalog(): MovieCatalog { ... }
// ...
}
----
======
With the preceding configuration, the following `MovieRecommender` is autowired with the
`firstMovieCatalog`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@Autowired
private MovieCatalog movieCatalog;
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@Autowired
private lateinit var movieCatalog: MovieCatalog
// ...
}
----
======
The corresponding bean definitions follow:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog" primary="true">
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>
----
@@ -0,0 +1,585 @@
[[beans-autowired-annotation-qualifiers]]
= Fine-tuning Annotation-based Autowiring with Qualifiers
`@Primary` is an effective way to use autowiring by type with several instances when one
primary candidate can be determined. When you need more control over the selection process,
you can use Spring's `@Qualifier` annotation. You can associate qualifier values
with specific arguments, narrowing the set of type matches so that a specific bean is
chosen for each argument. In the simplest case, this can be a plain descriptive value, as
shown in the following example:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@Autowired
@Qualifier("main")
private lateinit var movieCatalog: MovieCatalog
// ...
}
----
======
--
You can also specify the `@Qualifier` annotation on individual constructor arguments or
method parameters, as shown in the following example:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
private final MovieCatalog movieCatalog;
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
private lateinit var movieCatalog: MovieCatalog
private lateinit var customerPreferenceDao: CustomerPreferenceDao
@Autowired
fun prepare(@Qualifier("main") movieCatalog: MovieCatalog,
customerPreferenceDao: CustomerPreferenceDao) {
this.movieCatalog = movieCatalog
this.customerPreferenceDao = customerPreferenceDao
}
// ...
}
----
======
--
The following example shows corresponding bean definitions.
--
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog">
<qualifier value="main"/> <1>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<qualifier value="action"/> <2>
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>
----
<1> The bean with the `main` qualifier value is wired with the constructor argument that
is qualified with the same value.
<2> The bean with the `action` qualifier value is wired with the constructor argument that
is qualified with the same value.
--
For a fallback match, the bean name is considered a default qualifier value. Thus, you
can define the bean with an `id` of `main` instead of the nested qualifier element, leading
to the same matching result. However, although you can use this convention to refer to
specific beans by name, `@Autowired` is fundamentally about type-driven injection with
optional semantic qualifiers. This means that qualifier values, even with the bean name
fallback, always have narrowing semantics within the set of type matches. They do not
semantically express a reference to a unique bean `id`. Good qualifier values are `main`
or `EMEA` or `persistent`, expressing characteristics of a specific component that are
independent from the bean `id`, which may be auto-generated in case of an anonymous bean
definition such as the one in the preceding example.
Qualifiers also apply to typed collections, as discussed earlier -- for example, to
`Set<MovieCatalog>`. In this case, all matching beans, according to the declared
qualifiers, are injected as a collection. This implies that qualifiers do not have to be
unique. Rather, they constitute filtering criteria. For example, you can define
multiple `MovieCatalog` beans with the same qualifier value "`action`", all of which are
injected into a `Set<MovieCatalog>` annotated with `@Qualifier("action")`.
[TIP]
====
Letting qualifier values select against target bean names, within the type-matching
candidates, does not require a `@Qualifier` annotation at the injection point.
If there is no other resolution indicator (such as a qualifier or a primary marker),
for a non-unique dependency situation, Spring matches the injection point name
(that is, the field name or parameter name) against the target bean names and chooses the
same-named candidate, if any.
====
That said, if you intend to express annotation-driven injection by name, do not
primarily use `@Autowired`, even if it is capable of selecting by bean name among
type-matching candidates. Instead, use the JSR-250 `@Resource` annotation, which is
semantically defined to identify a specific target component by its unique name, with
the declared type being irrelevant for the matching process. `@Autowired` has rather
different semantics: After selecting candidate beans by type, the specified `String`
qualifier value is considered within those type-selected candidates only (for example,
matching an `account` qualifier against beans marked with the same qualifier label).
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, 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.
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`
is supported only for fields and bean property setter methods with a single argument.
As a consequence, you should stick with qualifiers if your injection target is a
constructor or a multi-argument method.
You can create your own custom qualifier annotations. To do so, define an annotation and
provide the `@Qualifier` annotation within your definition, as the following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class Genre(val value: String)
----
======
--
Then you can provide the custom qualifier on autowired fields and parameters, as the
following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@Autowired
@Genre("Action")
private MovieCatalog actionCatalog;
private MovieCatalog comedyCatalog;
@Autowired
public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
this.comedyCatalog = comedyCatalog;
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@Autowired
@Genre("Action")
private lateinit var actionCatalog: MovieCatalog
private lateinit var comedyCatalog: MovieCatalog
@Autowired
fun setComedyCatalog(@Genre("Comedy") comedyCatalog: MovieCatalog) {
this.comedyCatalog = comedyCatalog
}
// ...
}
----
======
--
Next, you can provide the information for the candidate bean definitions. You can add
`<qualifier/>` tags as sub-elements of the `<bean/>` tag and then specify the `type` and
`value` to match your custom qualifier annotations. The type is matched against the
fully-qualified class name of the annotation. Alternately, as a convenience if no risk of
conflicting names exists, you can use the short class name. The following example
demonstrates both approaches:
--
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog">
<qualifier type="Genre" value="Action"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<qualifier type="example.Genre" value="Comedy"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>
----
--
In xref:core/beans/classpath-scanning.adoc[Classpath Scanning and Managed Components], you can see an annotation-based alternative to
providing the qualifier metadata in XML. Specifically, see xref:core/beans/classpath-scanning.adoc#beans-scanning-qualifiers[Providing Qualifier Metadata with Annotations].
In some cases, using an annotation without a value may suffice. This can be
useful when the annotation serves a more generic purpose and can be applied across
several different types of dependencies. For example, you may provide an offline
catalog that can be searched when no Internet connection is available. First, define
the simple annotation, as the following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class Offline
----
======
--
Then add the annotation to the field or property to be autowired, as shown in the
following example:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@Autowired
@Offline // <1>
private MovieCatalog offlineCatalog;
// ...
}
----
<1> This line adds the `@Offline` annotation.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@Autowired
@Offline // <1>
private lateinit var offlineCatalog: MovieCatalog
// ...
}
----
<1> This line adds the `@Offline` annotation.
======
--
Now the bean definition only needs a qualifier `type`, as shown in the following example:
--
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean class="example.SimpleMovieCatalog">
<qualifier type="Offline"/> <1>
<!-- inject any dependencies required by this bean -->
</bean>
----
<1> This element specifies the qualifier.
--
You can also define custom qualifier annotations that accept named attributes in
addition to or instead of the simple `value` attribute. If multiple attribute values are
then specified on a field or parameter to be autowired, a bean definition must match
all such attribute values to be considered an autowire candidate. As an example,
consider the following annotation definition:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {
String genre();
Format format();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class MovieQualifier(val genre: String, val format: Format)
----
======
--
In this case `Format` is an enum, defined as follows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public enum Format {
VHS, DVD, BLURAY
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
enum class Format {
VHS, DVD, BLURAY
}
----
======
--
The fields to be autowired are annotated with the custom qualifier and include values
for both attributes: `genre` and `format`, as the following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@Autowired
@MovieQualifier(format=Format.VHS, genre="Action")
private MovieCatalog actionVhsCatalog;
@Autowired
@MovieQualifier(format=Format.VHS, genre="Comedy")
private MovieCatalog comedyVhsCatalog;
@Autowired
@MovieQualifier(format=Format.DVD, genre="Action")
private MovieCatalog actionDvdCatalog;
@Autowired
@MovieQualifier(format=Format.BLURAY, genre="Comedy")
private MovieCatalog comedyBluRayCatalog;
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@Autowired
@MovieQualifier(format = Format.VHS, genre = "Action")
private lateinit var actionVhsCatalog: MovieCatalog
@Autowired
@MovieQualifier(format = Format.VHS, genre = "Comedy")
private lateinit var comedyVhsCatalog: MovieCatalog
@Autowired
@MovieQualifier(format = Format.DVD, genre = "Action")
private lateinit var actionDvdCatalog: MovieCatalog
@Autowired
@MovieQualifier(format = Format.BLURAY, genre = "Comedy")
private lateinit var comedyBluRayCatalog: MovieCatalog
// ...
}
----
======
--
Finally, the bean definitions should contain matching qualifier values. This example
also demonstrates that you can use bean meta attributes instead of the
`<qualifier/>` elements. If available, the `<qualifier/>` element and its attributes take
precedence, but the autowiring mechanism falls back on the values provided within the
`<meta/>` tags if no such qualifier is present, as in the last two bean definitions in
the following example:
--
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog">
<qualifier type="MovieQualifier">
<attribute key="format" value="VHS"/>
<attribute key="genre" value="Action"/>
</qualifier>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<qualifier type="MovieQualifier">
<attribute key="format" value="VHS"/>
<attribute key="genre" value="Comedy"/>
</qualifier>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<meta key="format" value="DVD"/>
<meta key="genre" value="Action"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<meta key="format" value="BLURAY"/>
<meta key="genre" value="Comedy"/>
<!-- inject any dependencies required by this bean -->
</bean>
</beans>
----
--
@@ -0,0 +1,492 @@
[[beans-autowired-annotation]]
= Using `@Autowired`
[NOTE]
====
JSR 330's `@Inject` annotation can be used in place of Spring's `@Autowired` annotation in the
examples included in this section. See xref:core/beans/standard-annotations.adoc[here] for more details.
====
You can apply the `@Autowired` annotation to constructors, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender @Autowired constructor(
private val customerPreferenceDao: CustomerPreferenceDao)
----
======
[NOTE]
====
As of Spring Framework 4.3, an `@Autowired` annotation on such a constructor is no longer
necessary if the target bean defines only one constructor to begin with. However, if
several constructors are available and there is no primary/default constructor, at least
one of the constructors must be annotated with `@Autowired` in order to instruct the
container which one to use. See the discussion on
xref:core/beans/annotation-config/autowired.adoc#beans-autowired-annotation-constructor-resolution[constructor resolution] for details.
====
You can also apply the `@Autowired` annotation to _traditional_ setter methods,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SimpleMovieLister {
@set:Autowired
lateinit var movieFinder: MovieFinder
// ...
}
----
======
You can also apply the annotation to methods with arbitrary names and multiple
arguments, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
private lateinit var movieCatalog: MovieCatalog
private lateinit var customerPreferenceDao: CustomerPreferenceDao
@Autowired
fun prepare(movieCatalog: MovieCatalog,
customerPreferenceDao: CustomerPreferenceDao) {
this.movieCatalog = movieCatalog
this.customerPreferenceDao = customerPreferenceDao
}
// ...
}
----
======
You can apply `@Autowired` to fields as well and even mix it with constructors, as the
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
private MovieCatalog movieCatalog;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender @Autowired constructor(
private val customerPreferenceDao: CustomerPreferenceDao) {
@Autowired
private lateinit var movieCatalog: MovieCatalog
// ...
}
----
======
[TIP]
====
Make sure that your target components (for example, `MovieCatalog` or `CustomerPreferenceDao`)
are consistently declared by the type that you use for your `@Autowired`-annotated
injection points. Otherwise, injection may fail due to a "no type match found" error at runtime.
For XML-defined beans or component classes found via classpath scanning, the container
usually knows the concrete type up front. However, for `@Bean` factory methods, you need
to make sure that the declared return type is sufficiently expressive. For components
that implement several interfaces or for components potentially referred to by their
implementation type, consider declaring the most specific return type on your factory
method (at least as specific as required by the injection points referring to your bean).
====
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
expects an array of that type, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@Autowired
private MovieCatalog[] movieCatalogs;
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@Autowired
private lateinit var movieCatalogs: Array<MovieCatalog>
// ...
}
----
======
The same applies for typed collections, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
private Set<MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@Autowired
lateinit var movieCatalogs: Set<MovieCatalog>
// ...
}
----
======
[[beans-factory-ordered]]
[TIP]
====
Your target beans can implement the `org.springframework.core.Ordered` interface or use
the `@Order` or standard `@Priority` annotation if you want items in the array or list
to be sorted in a specific order. Otherwise, their order follows the registration
order of the corresponding target bean definitions in the container.
You can declare the `@Order` annotation at the target class level and on `@Bean` methods,
potentially for individual bean definitions (in case of multiple definitions that
use the same bean class). `@Order` values may influence priorities at injection points,
but be aware that they do not influence singleton startup order, which is an
orthogonal concern determined by dependency relationships and `@DependsOn` declarations.
Note that the standard `jakarta.annotation.Priority` annotation is not available at the
`@Bean` level, since it cannot be declared on methods. Its semantics can be modeled
through `@Order` values in combination with `@Primary` on a single bean for each type.
====
Even typed `Map` instances can be autowired as long as the expected key type is `String`.
The map values contain all beans of the expected type, and the keys contain the
corresponding bean names, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
private Map<String, MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@Autowired
lateinit var movieCatalogs: Map<String, MovieCatalog>
// ...
}
----
======
By default, autowiring fails when no matching candidate beans are available for a given
injection point. In the case of a declared array, collection, or map, at least one
matching element is expected.
The default behavior is to treat annotated methods and fields as indicating required
dependencies. You can change this behavior as demonstrated in the following example,
enabling the framework to skip a non-satisfiable injection point through marking it as
non-required (i.e., by setting the `required` attribute in `@Autowired` to `false`):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired(required = false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SimpleMovieLister {
@Autowired(required = false)
var movieFinder: MovieFinder? = null
// ...
}
----
======
[NOTE]
====
A non-required method will not be called at all if its dependency (or one of its
dependencies, in case of multiple arguments) is not available. A non-required field will
not get populated at all in such cases, leaving its default value in place.
In other words, setting the `required` attribute to `false` indicates that the
corresponding property is _optional_ for autowiring purposes, and the property will be
ignored if it cannot be autowired. This allows properties to be assigned default values
that can be optionally overridden via dependency injection.
====
[[beans-autowired-annotation-constructor-resolution]]
Injected constructor and factory method arguments are a special case since the `required`
attribute in `@Autowired` has a somewhat different meaning due to Spring's constructor
resolution algorithm that may potentially deal with multiple constructors. Constructor
and factory method arguments are effectively required by default but with a few special
rules in a single-constructor scenario, such as multi-element injection points (arrays,
collections, maps) resolving to empty instances if no matching beans are available. This
allows for a common implementation pattern where all dependencies can be declared in a
unique multi-argument constructor — for example, declared as a single public constructor
without an `@Autowired` annotation.
[NOTE]
====
Only one constructor of any given bean class may declare `@Autowired` with the `required`
attribute set to `true`, indicating _the_ constructor to autowire when used as a Spring
bean. As a consequence, if the `required` attribute is left at its default value `true`,
only a single constructor may be annotated with `@Autowired`. If multiple constructors
declare the annotation, they will all have to declare `required=false` in order to be
considered as candidates for autowiring (analogous to `autowire=constructor` in XML).
The constructor with the greatest number of dependencies that can be satisfied by matching
beans in the Spring container will be chosen. If none of the candidates can be satisfied,
then a primary/default constructor (if present) will be used. Similarly, if a class
declares multiple constructors but none of them is annotated with `@Autowired`, then a
primary/default constructor (if present) will be used. If a class only declares a single
constructor to begin with, it will always be used, even if not annotated. Note that an
annotated constructor does not have to be public.
====
Alternatively, you can express the non-required nature of a particular dependency
through Java 8's `java.util.Optional`, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
public class SimpleMovieLister {
@Autowired
public void setMovieFinder(Optional<MovieFinder> movieFinder) {
...
}
}
----
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",role="primary"]
----
public class SimpleMovieLister {
@Autowired
public void setMovieFinder(@Nullable MovieFinder movieFinder) {
...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SimpleMovieLister {
@Autowired
var movieFinder: MovieFinder? = null
// ...
}
----
======
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
interfaces, such as `ConfigurableApplicationContext` or `ResourcePatternResolver`, are
automatically resolved, with no special setup necessary. The following example autowires
an `ApplicationContext` object:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@Autowired
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@Autowired
lateinit var context: ApplicationContext
// ...
}
----
======
[NOTE]
====
The `@Autowired`, `@Inject`, `@Value`, and `@Resource` annotations are handled by Spring
`BeanPostProcessor` implementations. This means that you cannot apply these annotations
within your own `BeanPostProcessor` or `BeanFactoryPostProcessor` types (if any).
These types must be 'wired up' explicitly by using XML or a Spring `@Bean` method.
====
@@ -0,0 +1,33 @@
[[beans-custom-autowire-configurer]]
= Using `CustomAutowireConfigurer`
{api-spring-framework}/beans/factory/annotation/CustomAutowireConfigurer.html[`CustomAutowireConfigurer`]
is a `BeanFactoryPostProcessor` that lets you register your own custom qualifier
annotation types, even if they are not annotated with Spring's `@Qualifier` annotation.
The following example shows how to use `CustomAutowireConfigurer`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="customAutowireConfigurer"
class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>example.CustomQualifier</value>
</set>
</property>
</bean>
----
The `AutowireCandidateResolver` determines autowire candidates by:
* The `autowire-candidate` value of each bean definition
* Any `default-autowire-candidates` patterns available on the `<beans/>` element
* The presence of `@Qualifier` annotations and any custom annotations registered
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.
@@ -0,0 +1,101 @@
[[beans-generics-as-qualifiers]]
= Using Generics as Autowiring Qualifiers
In addition to the `@Qualifier` annotation, you can use Java generic types
as an implicit form of qualification. For example, suppose you have the following
configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class MyConfiguration {
@Bean
public StringStore stringStore() {
return new StringStore();
}
@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class MyConfiguration {
@Bean
fun stringStore() = StringStore()
@Bean
fun integerStore() = IntegerStore()
}
----
======
Assuming that the preceding beans implement a generic interface, (that is, `Store<String>` and
`Store<Integer>`), you can `@Autowire` the `Store` interface and the generic is
used as a qualifier, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Autowired
private lateinit var s1: Store<String> // <String> qualifier, injects the stringStore bean
@Autowired
private lateinit var s2: Store<Integer> // <Integer> qualifier, injects the integerStore bean
----
======
Generic qualifiers also apply when autowiring lists, `Map` instances and arrays. The
following example autowires a generic `List`:
[tabs]
======
Java::
+
[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
@Autowired
private List<Store<Integer>> s;
----
Kotlin::
+
[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
@Autowired
private lateinit var s: List<Store<Integer>>
----
======
@@ -0,0 +1,70 @@
[[beans-postconstruct-and-predestroy-annotations]]
= Using `@PostConstruct` and `@PreDestroy`
The `CommonAnnotationBeanPostProcessor` not only recognizes the `@Resource` annotation
but also the JSR-250 lifecycle annotations: `jakarta.annotation.PostConstruct` and
`jakarta.annotation.PreDestroy`. Introduced in Spring 2.5, the support for these
annotations offers an alternative to the lifecycle callback mechanism described in
xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-initializingbean[initialization callbacks] and
xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-disposablebean[destruction callbacks]. Provided that the
`CommonAnnotationBeanPostProcessor` is registered within the Spring `ApplicationContext`,
a method carrying one of these annotations is invoked at the same point in the lifecycle
as the corresponding Spring lifecycle interface method or explicitly declared callback
method. In the following example, the cache is pre-populated upon initialization and
cleared upon destruction:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class CachingMovieLister {
@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon initialization...
}
@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class CachingMovieLister {
@PostConstruct
fun populateMovieCache() {
// populates the movie cache upon initialization...
}
@PreDestroy
fun clearMovieCache() {
// clears the movie cache upon destruction...
}
}
----
======
For details about the effects of combining various lifecycle mechanisms, see
xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-combined-effects[Combining Lifecycle Mechanisms].
[NOTE]
====
Like `@Resource`, the `@PostConstruct` and `@PreDestroy` annotation types were a part
of the standard Java libraries from JDK 6 to 8. However, the entire `javax.annotation`
package got separated from the core Java modules in JDK 9 and eventually removed in
JDK 11. As of Jakarta EE 9, the package lives in `jakarta.annotation` now. If needed,
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.
====
@@ -0,0 +1,145 @@
[[beans-resource-annotation]]
= Injection with `@Resource`
Spring also supports injection by using the JSR-250 `@Resource` annotation
(`jakarta.annotation.Resource`) on fields or bean property setter methods.
This is a common pattern in Jakarta EE: for example, in JSF-managed beans and JAX-WS
endpoints. Spring supports this pattern for Spring-managed objects as well.
`@Resource` takes a name attribute. By default, Spring interprets that value as
the bean name to be injected. In other words, it follows by-name semantics,
as demonstrated in the following example:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder") // <1>
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
----
<1> This line injects a `@Resource`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SimpleMovieLister {
@Resource(name="myMovieFinder") // <1>
private lateinit var movieFinder:MovieFinder
}
----
<1> This line injects a `@Resource`.
======
--
If no name is explicitly specified, the default name is derived from the field name or
setter method. In case of a field, it takes the field name. In case of a setter method,
it takes the bean property name. The following example is going to have the bean
named `movieFinder` injected into its setter method:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SimpleMovieLister {
@set:Resource
private lateinit var movieFinder: MovieFinder
}
----
======
--
NOTE: The name provided with the annotation is resolved as a bean name by the
`ApplicationContext` of which the `CommonAnnotationBeanPostProcessor` is aware.
The names can be resolved through JNDI if you configure Spring's
{api-spring-framework}/jndi/support/SimpleJndiBeanFactory.html[`SimpleJndiBeanFactory`]
explicitly. However, we recommend that you rely on the default behavior and
use Spring's JNDI lookup capabilities to preserve the level of indirection.
In the exclusive case of `@Resource` usage with no explicit name specified, and similar
to `@Autowired`, `@Resource` finds a primary type match instead of a specific named bean
and resolves well known resolvable dependencies: the `BeanFactory`,
`ApplicationContext`, `ResourceLoader`, `ApplicationEventPublisher`, and `MessageSource`
interfaces.
Thus, in the following example, the `customerPreferenceDao` field first looks for a bean
named "customerPreferenceDao" and then falls back to a primary type match for the type
`CustomerPreferenceDao`:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MovieRecommender {
@Resource
private CustomerPreferenceDao customerPreferenceDao;
@Resource
private ApplicationContext context; // <1>
public MovieRecommender() {
}
// ...
}
----
<1> The `context` field is injected based on the known resolvable dependency type:
`ApplicationContext`.
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MovieRecommender {
@Resource
private lateinit var customerPreferenceDao: CustomerPreferenceDao
@Resource
private lateinit var context: ApplicationContext // <1>
// ...
}
----
<1> The `context` field is injected based on the known resolvable dependency type:
`ApplicationContext`.
======
--
@@ -0,0 +1,242 @@
[[beans-value-annotations]]
= Using `@Value`
`@Value` is typically used to inject externalized properties:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
public class MovieRecommender {
private final String catalog;
public MovieRecommender(@Value("${catalog.name}") String catalog) {
this.catalog = catalog;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
class MovieRecommender(@Value("\${catalog.name}") private val catalog: String)
----
======
With the following configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@PropertySource("classpath:application.properties")
class AppConfig
----
======
And the following `application.properties` file:
[source,java,indent=0,subs="verbatim,quotes"]
----
catalog.name=MovieCatalog
----
In that case, the `catalog` parameter and field will be equal to the `MovieCatalog` value.
A default lenient embedded value resolver is provided by Spring. It will try to resolve the
property value and if it cannot be resolved, the property name (for example `${catalog.name}`)
will be injected as the value. If you want to maintain strict control over nonexistent
values, you should declare a `PropertySourcesPlaceholderConfigurer` bean, as the following
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class AppConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class AppConfig {
@Bean
fun propertyPlaceholderConfigurer() = PropertySourcesPlaceholderConfigurer()
}
----
======
NOTE: When configuring a `PropertySourcesPlaceholderConfigurer` using JavaConfig, the
`@Bean` method must be `static`.
Using the above configuration ensures Spring initialization failure if any `${}`
placeholder could not be resolved. It is also possible to use methods like
`setPlaceholderPrefix`, `setPlaceholderSuffix`, or `setValueSeparator` to customize
placeholders.
NOTE: Spring Boot configures by default a `PropertySourcesPlaceholderConfigurer` bean that
will get properties from `application.properties` and `application.yml` files.
Built-in converter support provided by Spring allows simple type conversion (to `Integer`
or `int` for example) to be automatically handled. Multiple comma-separated values can be
automatically converted to `String` array without extra effort.
It is possible to provide a default value as following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
public class MovieRecommender {
private final String catalog;
public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) {
this.catalog = catalog;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
class MovieRecommender(@Value("\${catalog.name:defaultCatalog}") private val catalog: String)
----
======
A Spring `BeanPostProcessor` uses a `ConversionService` behind the scenes to handle the
process for converting the `String` value in `@Value` to the target type. If you want to
provide conversion support for your own custom type, you can provide your own
`ConversionService` bean instance as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
public class AppConfig {
@Bean
public ConversionService conversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
conversionService.addConverter(new MyCustomConverter());
return conversionService;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
class AppConfig {
@Bean
fun conversionService(): ConversionService {
return DefaultFormattingConversionService().apply {
addConverter(MyCustomConverter())
}
}
}
----
======
When `@Value` contains a xref:core/expressions.adoc[`SpEL` expression] the value will be dynamically
computed at runtime as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
public class MovieRecommender {
private final String catalog;
public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) {
this.catalog = catalog;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
class MovieRecommender(
@Value("#{systemProperties['user.catalog'] + 'Catalog' }") private val catalog: String)
----
======
SpEL also enables the use of more complex data structures:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Component
public class MovieRecommender {
private final Map<String, Integer> countOfMoviesPerCatalog;
public MovieRecommender(
@Value("#{{'Thriller': 100, 'Comedy': 300}}") Map<String, Integer> countOfMoviesPerCatalog) {
this.countOfMoviesPerCatalog = countOfMoviesPerCatalog;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Component
class MovieRecommender(
@Value("#{{'Thriller': 100, 'Comedy': 300}}") private val countOfMoviesPerCatalog: Map<String, Int>)
----
======
@@ -0,0 +1,420 @@
[[beans-basics]]
= Container Overview
The `org.springframework.context.ApplicationContext` interface represents the Spring IoC
container and is responsible for instantiating, configuring, and assembling the
beans. The container gets its instructions on what objects to
instantiate, configure, and assemble by reading configuration metadata. The
configuration metadata is represented in XML, Java annotations, or Java code. It lets
you express the objects that compose your application and the rich interdependencies
between those objects.
Several implementations of the `ApplicationContext` interface are supplied
with Spring. In stand-alone applications, it is common to create an
instance of
{api-spring-framework}/context/support/ClassPathXmlApplicationContext.html[`ClassPathXmlApplicationContext`]
or {api-spring-framework}/context/support/FileSystemXmlApplicationContext.html[`FileSystemXmlApplicationContext`].
While XML has been the traditional format for defining configuration metadata, you can
instruct the container to use Java annotations or code as the metadata format by
providing a small amount of XML configuration to declaratively enable support for these
additional metadata formats.
In most application scenarios, explicit user code is not required to instantiate one or
more instances of a Spring IoC container. For example, in a web application scenario, a
simple eight (or so) lines of boilerplate web descriptor XML in the `web.xml` file
of the application typically suffices (see xref:core/beans/context-introduction.adoc#context-create[Convenient ApplicationContext Instantiation for Web Applications]). If you use the
https://spring.io/tools[Spring Tools for Eclipse] (an Eclipse-powered development
environment), you can easily create this boilerplate configuration with a few mouse clicks or
keystrokes.
The following diagram shows a high-level view of how Spring works. Your application classes
are combined with configuration metadata so that, after the `ApplicationContext` is
created and initialized, you have a fully configured and executable system or
application.
.The Spring IoC container
image::container-magic.png[]
[[beans-factory-metadata]]
== Configuration Metadata
As the preceding diagram shows, the Spring IoC container consumes a form of
configuration metadata. This configuration metadata represents how you, as an
application developer, tell the Spring container to instantiate, configure, and assemble
the objects in your application.
Configuration metadata is traditionally supplied in a simple and intuitive XML format,
which is what most of this chapter uses to convey key concepts and features of the
Spring IoC container.
NOTE: XML-based metadata is not the only allowed form of configuration metadata.
The Spring IoC container itself is totally decoupled from the format in which this
configuration metadata is actually written. These days, many developers choose
xref:core/beans/java.adoc[Java-based configuration] for their Spring applications.
For information about using other forms of metadata with the Spring container, see:
* xref:core/beans/annotation-config.adoc[Annotation-based configuration]: define beans using
annotation-based configuration metadata.
* xref:core/beans/java.adoc[Java-based configuration]: define beans external to your application
classes by using Java rather than XML files. To use these features, see the
{api-spring-framework}/context/annotation/Configuration.html[`@Configuration`],
{api-spring-framework}/context/annotation/Bean.html[`@Bean`],
{api-spring-framework}/context/annotation/Import.html[`@Import`],
and {api-spring-framework}/context/annotation/DependsOn.html[`@DependsOn`] annotations.
Spring configuration consists of at least one and typically more than one bean
definition that the container must manage. XML-based configuration metadata configures these
beans as `<bean/>` elements inside a top-level `<beans/>` element. Java
configuration typically uses `@Bean`-annotated methods within a `@Configuration` class.
These bean definitions correspond to the actual objects that make up your application.
Typically, you define service layer objects, persistence layer objects such as
repositories or data access objects (DAOs), presentation objects such as Web controllers,
infrastructure objects such as a JPA `EntityManagerFactory`, JMS queues, and so forth.
Typically, one does not configure fine-grained domain objects in the container, because
it is usually the responsibility of repositories and business logic to create and load
domain objects.
The following example shows the basic structure of XML-based configuration metadata:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="..."> <1> <2>
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
----
<1> The `id` attribute is a string that identifies the individual bean definition.
<2> The `class` attribute defines the type of the bean and uses the fully qualified
class name.
The value of the `id` attribute can be used to refer to collaborating objects. The XML
for referring to collaborating objects is not shown in this example. See
xref:core/beans/dependencies.adoc[Dependencies] for more information.
[[beans-factory-instantiation]]
== Instantiating a Container
The location path or paths
supplied to an `ApplicationContext` constructor are resource strings that let
the container load configuration metadata from a variety of external resources, such
as the local file system, the Java `CLASSPATH`, and so on.
[tabs]
======
Java::
+
[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",role="secondary"]
----
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
----
======
[NOTE]
====
After you learn about Spring's IoC container, you may want to know more about Spring's
`Resource` abstraction (as described in xref:web/webflux-webclient/client-builder.adoc#webflux-client-builder-reactor-resources[Resources]), which provides a convenient
mechanism for reading an InputStream from locations defined in a URI syntax. In particular,
`Resource` paths are used to construct applications contexts, as described in xref:core/resources.adoc#resources-app-ctx[Application Contexts and Resource Paths].
====
The following example shows the service layer objects `(services.xml)` configuration file:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- services -->
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for services go here -->
</beans>
----
The following example shows the data access objects `daos.xml` file:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for data access objects go here -->
</beans>
----
In the preceding example, the service layer consists of the `PetStoreServiceImpl` class
and two data access objects of the types `JpaAccountDao` and `JpaItemDao` (based
on the JPA Object-Relational Mapping standard). The `property name` element refers to the
name of the JavaBean property, and the `ref` element refers to the name of another bean
definition. This linkage between `id` and `ref` elements expresses the dependency between
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
It can be useful to have bean definitions span multiple XML files. Often, each individual
XML configuration file represents a logical layer or module in your architecture.
You can use the application context constructor to load bean definitions from all these
XML fragments. This constructor takes multiple `Resource` locations, as was shown in the
xref:core/beans/basics.adoc#beans-factory-instantiation[previous section]. Alternatively, use one or more
occurrences of the `<import/>` element to load bean definitions from another file or
files. The following example shows how to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
----
In the preceding example, external bean definitions are loaded from three files:
`services.xml`, `messageSource.xml`, and `themeSource.xml`. All location paths are
relative to the definition file doing the importing, so `services.xml` must be in the
same directory or classpath location as the file doing the importing, while
`messageSource.xml` and `themeSource.xml` must be in a `resources` location below the
location of the importing file. As you can see, a leading slash is ignored. However, given
that these paths are relative, it is better form not to use the slash at all. The
contents of the files being imported, including the top level `<beans/>` element, must
be valid XML bean definitions, according to the Spring Schema.
[NOTE]
====
It is possible, but not recommended, to reference files in parent directories using a
relative "../" path. Doing so creates a dependency on a file that is outside the current
application. In particular, this reference is not recommended for `classpath:` URLs (for
example, `classpath:../services.xml`), where the runtime resolution process chooses the
"`nearest`" classpath root and then looks into its parent directory. Classpath
configuration changes may lead to the choice of a different, incorrect directory.
You can always use fully qualified resource locations instead of relative paths: for
example, `file:C:/config/services.xml` or `classpath:/config/services.xml`. However, be
aware that you are coupling your application's configuration to specific absolute
locations. It is generally preferable to keep an indirection for such absolute
locations -- for example, through "${...}" placeholders that are resolved against JVM
system properties at runtime.
====
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.
[[groovy-bean-definition-dsl]]
=== The Groovy Bean Definition DSL
As a further example for externalized configuration metadata, bean definitions can also
be expressed in Spring's Groovy Bean Definition DSL, as known from the Grails framework.
Typically, such configuration live in a ".groovy" file with the structure shown in the
following example:
[source,groovy,indent=0,subs="verbatim,quotes"]
----
beans {
dataSource(BasicDataSource) {
driverClassName = "org.hsqldb.jdbcDriver"
url = "jdbc:hsqldb:mem:grailsDB"
username = "sa"
password = ""
settings = [mynew:"setting"]
}
sessionFactory(SessionFactory) {
dataSource = dataSource
}
myService(MyService) {
nestedBean = { AnotherBean bean ->
dataSource = dataSource
}
}
}
----
This configuration style is largely equivalent to XML bean definitions and even
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
The `ApplicationContext` is the interface for an advanced factory capable of maintaining
a registry of different beans and their dependencies. By using the method
`T getBean(String name, Class<T> requiredType)`, you can retrieve instances of your beans.
The `ApplicationContext` lets you read bean definitions and access them, as the following
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
import org.springframework.beans.factory.getBean
// create and configure beans
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
// retrieve configured instance
val service = context.getBean<PetStoreService>("petStore")
// use configured instance
var userList = service.getUsernameList()
----
======
With Groovy configuration, bootstrapping looks very similar. It has a different context
implementation class which is Groovy-aware (but also understands XML bean definitions).
The following example shows Groovy configuration:
[tabs]
======
Java::
+
[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",role="secondary"]
----
val context = GenericGroovyApplicationContext("services.groovy", "daos.groovy")
----
======
The most flexible variant is `GenericApplicationContext` in combination with reader
delegates -- for example, with `XmlBeanDefinitionReader` for XML files, as the following
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val context = GenericApplicationContext()
XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml")
context.refresh()
----
======
You can also use the `GroovyBeanDefinitionReader` for Groovy files, as the following
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val context = GenericApplicationContext()
GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy")
context.refresh()
----
======
You can mix and match such reader delegates on the same `ApplicationContext`,
reading bean definitions from diverse configuration sources.
You can then use `getBean` to retrieve instances of your beans. The `ApplicationContext`
interface has a few other methods for retrieving beans, but, ideally, your application
code should never use them. Indeed, your application code should have no calls to the
`getBean()` method at all and thus have no dependency on Spring APIs at all. For example,
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).
@@ -0,0 +1,171 @@
[[beans-beanfactory]]
= The `BeanFactory` API
The `BeanFactory` API provides the underlying basis for Spring's IoC functionality.
Its specific contracts are mostly used in integration with other parts of Spring and
related third-party frameworks, and its `DefaultListableBeanFactory` implementation
is a key delegate within the higher-level `GenericApplicationContext` container.
`BeanFactory` and related interfaces (such as `BeanFactoryAware`, `InitializingBean`,
`DisposableBean`) are important integration points for other framework components.
By not requiring any annotations or even reflection, they allow for very efficient
interaction between the container and its components. Application-level beans may
use the same callback interfaces but typically prefer declarative dependency
injection instead, either through annotations or through programmatic configuration.
Note that the core `BeanFactory` API level and its `DefaultListableBeanFactory`
implementation do not make assumptions about the configuration format or any
component annotations to be used. All of these flavors come in through extensions
(such as `XmlBeanDefinitionReader` and `AutowiredAnnotationBeanPostProcessor`) and
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`?
This section explains the differences between the `BeanFactory` and
`ApplicationContext` container levels and the implications on bootstrapping.
You should use an `ApplicationContext` unless you have a good reason for not doing so, with
`GenericApplicationContext` and its subclass `AnnotationConfigApplicationContext`
as the common implementations for custom bootstrapping. These are the primary entry
points to Spring's core container for all common purposes: loading of configuration
files, triggering a classpath scan, programmatically registering bean definitions
and annotated classes, and (as of 5.0) registering functional bean definitions.
Because an `ApplicationContext` includes all the functionality of a `BeanFactory`, it is
generally recommended over a plain `BeanFactory`, except for scenarios where full
control over bean processing is needed. Within an `ApplicationContext` (such as the
`GenericApplicationContext` implementation), several kinds of beans are detected
by convention (that is, by bean name or by bean type -- in particular, post-processors),
while a plain `DefaultListableBeanFactory` is agnostic about any special beans.
For many extended container features, such as annotation processing and AOP proxying,
the xref:core/beans/factory-extension.adoc#beans-factory-extension-bpp[`BeanPostProcessor` extension point] is essential.
If you use only a plain `DefaultListableBeanFactory`, such post-processors do not
get detected and activated by default. This situation could be confusing, because
nothing is actually wrong with your bean configuration. Rather, in such a scenario,
the container needs to be fully bootstrapped through additional setup.
The following table lists features provided by the `BeanFactory` and
`ApplicationContext` interfaces and implementations.
[[context-introduction-ctx-vs-beanfactory-feature-matrix]]
.Feature Matrix
[cols="50%,25%,25%"]
|===
| Feature | `BeanFactory` | `ApplicationContext`
| Bean instantiation/wiring
| Yes
| Yes
| Integrated lifecycle management
| No
| Yes
| Automatic `BeanPostProcessor` registration
| No
| Yes
| Automatic `BeanFactoryPostProcessor` registration
| No
| Yes
| Convenient `MessageSource` access (for internationalization)
| No
| Yes
| Built-in `ApplicationEvent` publication mechanism
| No
| Yes
|===
To explicitly register a bean post-processor with a `DefaultListableBeanFactory`,
you need to programmatically call `addBeanPostProcessor`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// populate the factory with bean definitions
// now register any needed BeanPostProcessor instances
factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
factory.addBeanPostProcessor(new MyBeanPostProcessor());
// now start using the factory
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val factory = DefaultListableBeanFactory()
// populate the factory with bean definitions
// now register any needed BeanPostProcessor instances
factory.addBeanPostProcessor(AutowiredAnnotationBeanPostProcessor())
factory.addBeanPostProcessor(MyBeanPostProcessor())
// now start using the factory
----
======
To apply a `BeanFactoryPostProcessor` to a plain `DefaultListableBeanFactory`,
you need to call its `postProcessBeanFactory` method, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(new FileSystemResource("beans.xml"));
// bring in some property values from a Properties file
PropertySourcesPlaceholderConfigurer cfg = new PropertySourcesPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
// now actually do the replacement
cfg.postProcessBeanFactory(factory);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val factory = DefaultListableBeanFactory()
val reader = XmlBeanDefinitionReader(factory)
reader.loadBeanDefinitions(FileSystemResource("beans.xml"))
// bring in some property values from a Properties file
val cfg = PropertySourcesPlaceholderConfigurer()
cfg.setLocation(FileSystemResource("jdbc.properties"))
// now actually do the replacement
cfg.postProcessBeanFactory(factory)
----
======
In both cases, the explicit registration steps are inconvenient, which is
why the various `ApplicationContext` variants are preferred over a plain
`DefaultListableBeanFactory` in Spring-backed applications, especially when
relying on `BeanFactoryPostProcessor` and `BeanPostProcessor` instances for extended
container functionality in a typical enterprise setup.
[NOTE]
====
An `AnnotationConfigApplicationContext` has all common annotation post-processors
registered and may bring in additional processors underneath the
covers through configuration annotations, such as `@EnableTransactionManagement`.
At the abstraction level of Spring's annotation-based configuration model,
the notion of bean post-processors becomes a mere internal container detail.
====
@@ -0,0 +1,84 @@
[[beans-child-bean-definitions]]
= Bean Definition Inheritance
A bean definition can contain a lot of configuration information, including constructor
arguments, property values, and container-specific information, such as the initialization
method, a static factory method name, and so on. A child bean definition inherits
configuration data from a parent definition. The child definition can override some
values or add others as needed. Using parent and child bean definitions can save a lot
of typing. Effectively, this is a form of templating.
If you work with an `ApplicationContext` interface programmatically, child bean
definitions are represented by the `ChildBeanDefinition` class. Most users do not work
with them on this level. Instead, they configure bean definitions declaratively in a class
such as the `ClassPathXmlApplicationContext`. When you use XML-based configuration
metadata, you can indicate a child bean definition by using the `parent` attribute,
specifying the parent bean as the value of this attribute. The following example shows how
to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="inheritedTestBean" abstract="true"
class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass"
class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize"> <1>
<property name="name" value="override"/>
<!-- the age property value of 1 will be inherited from parent -->
</bean>
----
<1> Note the `parent` attribute.
A child bean definition uses the bean class from the parent definition if none is
specified but can also override it. In the latter case, the child bean class must be
compatible with the parent (that is, it must accept the parent's property values).
A child bean definition inherits scope, constructor argument values, property values, and
method overrides from the parent, with the option to add new values. Any scope, initialization
method, destroy method, or `static` factory method settings that you specify
override the corresponding parent settings.
The remaining settings are always taken from the child definition: depends on,
autowire mode, dependency check, singleton, and lazy init.
The preceding example explicitly marks the parent bean definition as abstract by using
the `abstract` attribute. If the parent definition does not specify a class, explicitly
marking the parent bean definition as `abstract` is required, as the following example
shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="inheritedTestBeanWithoutClass" abstract="true">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBeanWithoutClass" init-method="initialize">
<property name="name" value="override"/>
<!-- age will inherit the value of 1 from the parent bean definition-->
</bean>
----
The parent bean cannot be instantiated on its own because it is incomplete, and it is
also explicitly marked as `abstract`. When a definition is `abstract`, it is
usable only as a pure template bean definition that serves as a parent definition for
child definitions. Trying to use such an `abstract` parent bean on its own, by referring
to it as a ref property of another bean or doing an explicit `getBean()` call with the
parent bean ID returns an error. Similarly, the container's internal
`preInstantiateSingletons()` method ignores bean definitions that are defined as
abstract.
NOTE: `ApplicationContext` pre-instantiates all singletons by default. Therefore, it is
important (at least for singleton beans) that if you have a (parent) bean definition
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.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,52 @@
[[context-load-time-weaver]]
= Registering a `LoadTimeWeaver`
The `LoadTimeWeaver` is used by Spring to dynamically transform classes as they are
loaded into the Java virtual machine (JVM).
To enable load-time weaving, you can add the `@EnableLoadTimeWeaving` to one of your
`@Configuration` classes, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@Configuration
@EnableLoadTimeWeaving
class AppConfig
----
======
Alternatively, for XML configuration, you can use the `context:load-time-weaver` element:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<beans>
<context:load-time-weaver/>
</beans>
----
Once configured for the `ApplicationContext`, any bean within that `ApplicationContext`
may implement `LoadTimeWeaverAware`, thereby receiving a reference to the load-time
weaver instance. This is particularly useful in combination with
xref:data-access/orm/jpa.adoc[Spring's JPA support] where load-time weaving may be
necessary for JPA class transformation.
Consult the
{api-spring-framework}/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].
@@ -0,0 +1,448 @@
[[beans-definition]]
= Bean Overview
A Spring IoC container manages one or more beans. These beans are created with the
configuration metadata that you supply to the container (for example, in the form of XML
`<bean/>` definitions).
Within the container itself, these bean definitions are represented as `BeanDefinition`
objects, which contain (among other information) the following metadata:
* A package-qualified class name: typically, the actual implementation class of the
bean being defined.
* Bean behavioral configuration elements, which state how the bean should behave in the
container (scope, lifecycle callbacks, and so forth).
* References to other beans that are needed for the bean to do its work. These
references are also called collaborators or dependencies.
* Other configuration settings to set in the newly created object -- for example, the size
limit of the pool or the number of connections to use in a bean that manages a
connection pool.
This metadata translates to a set of properties that make up each bean definition.
The following table describes these properties:
[[beans-factory-bean-definition-tbl]]
.The bean definition
|===
| Property| Explained in...
| Class
| xref:core/beans/definition.adoc#beans-factory-class[Instantiating Beans]
| Name
| xref:core/beans/definition.adoc#beans-beanname[Naming Beans]
| Scope
| xref:core/beans/factory-scopes.adoc[Bean Scopes]
| Constructor arguments
| xref:core/beans/dependencies/factory-collaborators.adoc[Dependency Injection]
| Properties
| xref:core/beans/dependencies/factory-collaborators.adoc[Dependency Injection]
| Autowiring mode
| xref:core/beans/dependencies/factory-autowire.adoc[Autowiring Collaborators]
| Lazy initialization mode
| xref:core/beans/dependencies/factory-lazy-init.adoc[Lazy-initialized Beans]
| Initialization method
| xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-initializingbean[Initialization Callbacks]
| Destruction method
| xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-disposablebean[Destruction Callbacks]
|===
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 `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.
[NOTE]
====
Bean metadata and manually supplied singleton instances need to be registered as early
as possible, in order for the container to properly reason about them during autowiring
and other introspection steps. While overriding existing metadata and existing
singleton instances is supported to some degree, the registration of new beans at
runtime (concurrently with live access to the factory) is not officially supported and may
lead to concurrent access exceptions, inconsistent state in the bean container, or both.
====
[[beans-beanname]]
== Naming Beans
Every bean has one or more identifiers. These identifiers must be unique within the
container that hosts the bean. A bean usually has only one identifier. However, if it
requires more than one, the extra ones can be considered aliases.
In XML-based configuration metadata, you use the `id` attribute, the `name` attribute, or
both to specify bean identifiers. The `id` attribute lets you specify exactly one `id`.
Conventionally, these names are alphanumeric ('myBean', 'someService', etc.), but they
can contain special characters as well. If you want to introduce other aliases for the
bean, you can also specify them in the `name` attribute, separated by a comma (`,`),
semicolon (`;`), or white space. Although the `id` attribute is defined as an
`xsd:string` type, bean `id` uniqueness is enforced by the container, though not by XML
parsers.
You are not required to supply a `name` or an `id` for a bean. If you do not supply a
`name` or `id` explicitly, the container generates a unique name for that bean. However,
if you want to refer to that bean by name, through the use of the `ref` element or a
Service Locator style lookup, you must provide a name.
Motivations for not supplying a name are related to using xref:core/beans/dependencies/factory-properties-detailed.adoc#beans-inner-beans[inner beans]
and xref:core/beans/dependencies/factory-autowire.adoc[autowiring collaborators].
.Bean Naming Conventions
****
The convention is to use the standard Java convention for instance field names when
naming beans. That is, bean names start with a lowercase letter and are camel-cased
from there. Examples of such names include `accountManager`,
`accountService`, `userDao`, `loginController`, and so forth.
Naming beans consistently makes your configuration easier to read and understand.
Also, if you use Spring AOP, it helps a lot when applying advice to a set of beans
related by name.
****
NOTE: With component scanning in the classpath, Spring generates bean names for unnamed
components, following the rules described earlier: essentially, taking the simple class name
and turning its initial character to lower-case. However, in the (unusual) special
case when there is more than one character and both the first and second characters
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
In a bean definition itself, you can supply more than one name for the bean, by using a
combination of up to one name specified by the `id` attribute and any number of other
names in the `name` attribute. These names can be equivalent aliases to the same bean
and are useful for some situations, such as letting each component in an application
refer to a common dependency by using a bean name that is specific to that component
itself.
Specifying all aliases where the bean is actually defined is not always adequate,
however. It is sometimes desirable to introduce an alias for a bean that is defined
elsewhere. This is commonly the case in large systems where configuration is split
amongst each subsystem, with each subsystem having its own set of object definitions.
In XML-based configuration metadata, you can use the `<alias/>` element to accomplish
this. The following example shows how to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<alias name="fromName" alias="toName"/>
----
In this case, a bean (in the same container) named `fromName` may also,
after the use of this alias definition, be referred to as `toName`.
For example, the configuration metadata for subsystem A may refer to a DataSource by the
name of `subsystemA-dataSource`. The configuration metadata for subsystem B may refer to
a DataSource by the name of `subsystemB-dataSource`. When composing the main application
that uses both these subsystems, the main application refers to the DataSource by the
name of `myApp-dataSource`. To have all three names refer to the same object, you can
add the following alias definitions to the configuration metadata:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
----
Now each component and the main application can refer to the dataSource through a name
that is unique and guaranteed not to clash with any other definition (effectively
creating a namespace), yet they refer to the same bean.
.Java-configuration
****
If you use Java Configuration, the `@Bean` annotation can be used to provide aliases.
See xref:core/beans/java/bean-annotation.adoc[Using the `@Bean` Annotation] for details.
****
[[beans-factory-class]]
== Instantiating Beans
A bean definition is essentially a recipe for creating one or more objects. The
container looks at the recipe for a named bean when asked and uses the configuration
metadata encapsulated by that bean definition to create (or acquire) an actual object.
If you use XML-based configuration metadata, you specify the type (or class) of object
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].)
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
itself directly creates the bean by calling its constructor reflectively, somewhat
equivalent to Java code with the `new` operator.
* To specify the actual class containing the `static` factory method that is
invoked to create the object, in the less common case where the container invokes a
`static` factory method on a class to create the bean. The object type returned
from the invocation of the `static` factory method may be the same class or another
class entirely.
.Nested class names
****
If you want to configure a bean definition for a nested class, you may use either the
binary name or the source name of the nested class.
For example, if you have a class called `SomeThing` in the `com.example` package, and
this `SomeThing` class has a `static` nested class called `OtherThing`, they can be
separated by a dollar sign (`$`) or a dot (`.`). So the value of the `class` attribute in
a bean definition would be `com.example.SomeThing$OtherThing` or
`com.example.SomeThing.OtherThing`.
****
[[beans-factory-class-ctor]]
=== Instantiation with a Constructor
When you create a bean by the constructor approach, all normal classes are usable by and
compatible with Spring. That is, the class being developed does not need to implement
any specific interfaces or to be coded in a specific fashion. Simply specifying the bean
class should suffice. However, depending on what type of IoC you use for that specific
bean, you may need a default (empty) constructor.
The Spring IoC container can manage virtually any class you want it to manage. It is
not limited to managing true JavaBeans. Most Spring users prefer actual JavaBeans with
only a default (no-argument) constructor and appropriate setters and getters modeled
after the properties in the container. You can also have more exotic non-bean-style
classes in your container. If, for example, you need to use a legacy connection pool
that absolutely does not adhere to the JavaBean specification, Spring can manage it as
well.
With XML-based configuration metadata you can specify your bean class as follows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
----
For details about the mechanism for supplying arguments to the constructor (if required)
and setting object instance properties after the object is constructed, see
xref:core/beans/dependencies/factory-collaborators.adoc[Injecting Dependencies].
[[beans-factory-class-static-factory-method]]
=== Instantiation with a Static Factory Method
When defining a bean that you create with a static factory method, use the `class`
attribute to specify the class that contains the `static` factory method and an attribute
named `factory-method` to specify the name of the factory method itself. You should be
able to call this method (with optional arguments, as described later) and return a live
object, which subsequently is treated as if it had been created through a constructor.
One use for such a bean definition is to call `static` factories in legacy code.
The following bean definition specifies that the bean will be created by calling a
factory method. The definition does not specify the type (class) of the returned object,
but rather the class containing the factory method. In this example, the
`createInstance()` method must be a `static` method. The following example shows how to
specify a factory method:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
----
The following example shows a class that would work with the preceding bean definition:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class ClientService private constructor() {
companion object {
private val clientService = ClientService()
@JvmStatic
fun createInstance() = clientService
}
}
----
======
For details about the mechanism for supplying (optional) arguments to the factory method
and setting object instance properties after the object is returned from the factory,
see xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies and Configuration in Detail].
[[beans-factory-class-instance-factory-method]]
=== Instantiation by Using an Instance Factory Method
Similar to instantiation through a xref:core/beans/definition.adoc#beans-factory-class-static-factory-method[static factory method]
, instantiation with an instance factory method invokes a non-static
method of an existing bean from the container to create a new bean. To use this
mechanism, leave the `class` attribute empty and, in the `factory-bean` attribute,
specify the name of a bean in the current (or parent or ancestor) container that contains
the instance method that is to be invoked to create the object. Set the name of the
factory method itself with the `factory-method` attribute. The following example shows
how to configure such a bean:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
----
The following example shows the corresponding class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class DefaultServiceLocator {
companion object {
private val clientService = ClientServiceImpl()
}
fun createClientServiceInstance(): ClientService {
return clientService
}
}
----
======
One factory class can also hold more than one factory method, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
<bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
----
The following example shows the corresponding class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class DefaultServiceLocator {
companion object {
private val clientService = ClientServiceImpl()
private val accountService = AccountServiceImpl()
}
fun createClientServiceInstance(): ClientService {
return clientService
}
fun createAccountServiceInstance(): AccountService {
return accountService
}
}
----
======
This approach shows that the factory bean itself can be managed and configured through
dependency injection (DI). See xref:core/beans/dependencies/factory-properties-detailed.adoc[Dependencies and Configuration in Detail]
.
NOTE: In Spring documentation, "factory bean" refers to a bean that is configured in the
Spring container and that creates objects through an
xref:core/beans/definition.adoc#beans-factory-class-instance-factory-method[instance] or
xref:core/beans/definition.adoc#beans-factory-class-static-factory-method[static] factory method. By contrast,
`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
The runtime type of a specific bean is non-trivial to determine. A specified class in
the bean metadata definition is just an initial class reference, potentially combined
with a declared factory method or being a `FactoryBean` class which may lead to a
different runtime type of the bean, or not being set at all in case of an instance-level
factory method (which is resolved via the specified `factory-bean` name instead).
Additionally, AOP proxying may wrap a bean instance with an interface-based proxy with
limited exposure of the target bean's actual type (just its implemented interfaces).
The recommended way to find out about the actual runtime type of a particular bean is
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.
@@ -0,0 +1,12 @@
[[beans-dependencies]]
= Dependencies
:page-section-summary-toc: 1
A typical enterprise application does not consist of a single object (or bean in the
Spring parlance). Even the simplest application has a few objects that work together to
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.
@@ -0,0 +1,127 @@
[[beans-factory-autowire]]
= Autowiring Collaborators
The Spring container can autowire relationships between collaborating beans. You can
let Spring resolve collaborators (other beans) automatically for your bean by
inspecting the contents of the `ApplicationContext`. Autowiring has the following
advantages:
* Autowiring can significantly reduce the need to specify properties or constructor
arguments. (Other mechanisms such as a bean template
xref:core/beans/child-bean-definitions.adoc[discussed elsewhere in this chapter] are also valuable
in this regard.)
* Autowiring can update a configuration as your objects evolve. For example, if you need
to add a dependency to a class, that dependency can be satisfied automatically without
you needing to modify the configuration. Thus autowiring can be especially useful
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
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
four autowiring modes:
[[beans-factory-autowiring-modes-tbl]]
.Autowiring modes
[cols="20%,80%"]
|===
| Mode| Explanation
| `no`
| (Default) No autowiring. Bean references must be defined by `ref` elements. Changing
the default setting is not recommended for larger deployments, because specifying
collaborators explicitly gives greater control and clarity. To some extent, it
documents the structure of a system.
| `byName`
| Autowiring by property name. Spring looks for a bean with the same name as the
property that needs to be autowired. For example, if a bean definition is set to
autowire by name and it contains a `master` property (that is, it has a
`setMaster(..)` method), Spring looks for a bean definition named `master` and uses
it to set the property.
| `byType`
| Lets a property be autowired if exactly one bean of the property type exists in
the container. If more than one exists, a fatal exception is thrown, which indicates
that you may not use `byType` autowiring for that bean. If there are no matching
beans, nothing happens (the property is not set).
| `constructor`
| Analogous to `byType` but applies to constructor arguments. If there is not exactly
one bean of the constructor argument type in the container, a fatal error is raised.
|===
With `byType` or `constructor` autowiring mode, you can wire arrays and
typed collections. In such cases, all autowire candidates within the container that
match the expected type are provided to satisfy the dependency. You can autowire
strongly-typed `Map` instances if the expected key type is `String`. An autowired `Map`
instance's values consist of all bean instances that match the expected type, and the
`Map` instance's keys contain the corresponding bean names.
[[beans-autowired-exceptions]]
== Limitations and Disadvantages of Autowiring
Autowiring works best when it is used consistently across a project. If autowiring is
not used in general, it might be confusing to developers to use it to wire only one or
two bean definitions.
Consider the limitations and disadvantages of autowiring:
* Explicit dependencies in `property` and `constructor-arg` settings always override
autowiring. You cannot autowire simple properties such as primitives,
`Strings`, and `Classes` (and arrays of such simple properties). This limitation is
by-design.
* Autowiring is less exact than explicit wiring. Although, as noted in the earlier table,
Spring is careful to avoid guessing in case of ambiguity that might have unexpected
results. The relationships between your Spring-managed objects are no longer
documented explicitly.
* Wiring information may not be available to tools that may generate documentation from
a Spring container.
* Multiple bean definitions within the container may match the type specified by the
setter method or constructor argument to be autowired. For arrays, collections, or
`Map` instances, this is not necessarily a problem. However, for dependencies that
expect a single value, this ambiguity is not arbitrarily resolved. If no unique bean
definition is available, an exception is thrown.
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].
* 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
On a per-bean basis, you can exclude a bean from autowiring. In Spring's XML format, set
the `autowire-candidate` attribute of the `<bean/>` element to `false`. The container
makes that specific bean definition unavailable to the autowiring infrastructure
(including annotation style configurations such as xref:core/beans/annotation-config/autowired.adoc[`@Autowired`]
).
NOTE: The `autowire-candidate` attribute is designed to only affect type-based autowiring.
It does not affect explicit references by name, which get resolved even if the
specified bean is not marked as an autowire candidate. As a consequence, autowiring
by name nevertheless injects a bean if the name matches.
You can also limit autowire candidates based on pattern-matching against bean names. The
top-level `<beans/>` element accepts one or more patterns within its
`default-autowire-candidates` attribute. For example, to limit autowire candidate status
to any bean whose name ends with `Repository`, provide a value of `*Repository`. To
provide multiple patterns, define them in a comma-separated list. An explicit value of
`true` or `false` for a bean definition's `autowire-candidate` attribute always takes
precedence. For such beans, the pattern matching rules do not apply.
These techniques are useful for beans that you never want to be injected into other
beans by autowiring. It does not mean that an excluded bean cannot itself be configured by
using autowiring. Rather, the bean itself is not a candidate for autowiring other beans.
@@ -0,0 +1,608 @@
[[beans-factory-collaborators]]
= Dependency Injection
Dependency injection (DI) is a process whereby objects define their dependencies
(that is, the other objects with which they work) only through constructor arguments,
arguments to a factory method, or properties that are set on the object instance after
it is constructed or returned from a factory method. The container then injects those
dependencies when it creates the bean. This process is fundamentally the inverse (hence
the name, Inversion of Control) of the bean itself controlling the instantiation
or location of its dependencies on its own by using direct construction of classes or
the Service Locator pattern.
Code is cleaner with the DI principle, and decoupling is more effective when objects are
provided with their dependencies. The object does not look up its dependencies and does
not know the location or class of the dependencies. As a result, your classes become easier
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].
[[beans-constructor-injection]]
== Constructor-based Dependency Injection
Constructor-based DI is accomplished by the container invoking a constructor with a
number of arguments, each representing a dependency. Calling a `static` factory method
with specific arguments to construct the bean is nearly equivalent, and this discussion
treats arguments to a constructor and to a `static` factory method similarly. The
following example shows a class that can only be dependency-injected with constructor
injection:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on a MovieFinder
private final MovieFinder movieFinder;
// a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
----
Kotlin::
+
[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) {
// business logic that actually uses the injected MovieFinder is omitted...
}
----
======
Notice that there is nothing special about this class. It is a POJO that
has no dependencies on container specific interfaces, base classes, or annotations.
[[beans-factory-ctor-arguments-resolution]]
=== Constructor Argument Resolution
Constructor argument resolution matching occurs by using the argument's type. If no
potential ambiguity exists in the constructor arguments of a bean definition, the
order in which the constructor arguments are defined in a bean definition is the order
in which those arguments are supplied to the appropriate constructor when the bean is
being instantiated. Consider the following class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package x.y;
public class ThingOne {
public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
// ...
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package x.y
class ThingOne(thingTwo: ThingTwo, thingThree: ThingThree)
----
======
Assuming that the `ThingTwo` and `ThingThree` classes are not related by inheritance, no
potential ambiguity exists. Thus, the following configuration works fine, and you do not
need to specify the constructor argument indexes or types explicitly in the
`<constructor-arg/>` element.
[source,xml,indent=0,subs="verbatim,quotes"]
----
<beans>
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg ref="beanTwo"/>
<constructor-arg ref="beanThree"/>
</bean>
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
</beans>
----
When another bean is referenced, the type is known, and matching can occur (as was the
case with the preceding example). When a simple type is used, such as
`<value>true</value>`, Spring cannot determine the type of the value, and so cannot match
by type without help. Consider the following class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package examples;
public class ExampleBean {
// Number of years to calculate the Ultimate Answer
private final int years;
// The Answer to Life, the Universe, and Everything
private final String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package examples
class ExampleBean(
private val years: Int, // Number of years to calculate the Ultimate Answer
private val ultimateAnswer: String // The Answer to Life, the Universe, and Everything
)
----
======
.[[beans-factory-ctor-arguments-type]]Constructor argument type matching
--
In the preceding scenario, the container can use type matching with simple types if
you explicitly specify the type of the constructor argument by using the `type` attribute,
as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
----
--
.[[beans-factory-ctor-arguments-index]]Constructor argument index
--
You can use the `index` attribute to specify explicitly the index of constructor arguments,
as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
----
In addition to resolving the ambiguity of multiple simple values, specifying an index
resolves ambiguity where a constructor has two arguments of the same type.
NOTE: The index is 0-based.
--
.[[beans-factory-ctor-arguments-name]]Constructor argument name
--
You can also use the constructor parameter name for value disambiguation, as the following
example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
----
Keep in mind that, to make this work out of the box, your code must be compiled with the
debug flag enabled so that Spring can look up the parameter name from the constructor.
If you cannot or do not want to compile your code with the debug flag, you can use the
https://download.oracle.com/javase/8/docs/api/java/beans/ConstructorProperties.html[@ConstructorProperties]
JDK annotation to explicitly name your constructor arguments. The sample class would
then have to look as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
----
package examples;
public class ExampleBean {
// Fields omitted
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
----
package examples
class ExampleBean
@ConstructorProperties("years", "ultimateAnswer")
constructor(val years: Int, val ultimateAnswer: String)
----
======
--
[[beans-setter-injection]]
== Setter-based Dependency Injection
Setter-based DI is accomplished by the container calling setter methods on your
beans after invoking a no-argument constructor or a no-argument `static` factory method to
instantiate your bean.
The following example shows a class that can only be dependency-injected by using pure
setter injection. This class is conventional Java. It is a POJO that has no dependencies
on container specific interfaces, base classes, or annotations.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder;
// a setter method so that the Spring container can inject a MovieFinder
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class SimpleMovieLister {
// a late-initialized property so that the Spring container can inject a MovieFinder
lateinit var movieFinder: MovieFinder
// business logic that actually uses the injected MovieFinder is omitted...
}
----
======
The `ApplicationContext` supports constructor-based and setter-based DI for the beans it
manages. It also supports setter-based DI after some dependencies have already been
injected through the constructor approach. You configure the dependencies in the form of
a `BeanDefinition`, which you use in conjunction with `PropertyEditor` instances to
convert properties from one format to another. However, most Spring users do not work
with these classes directly (that is, programmatically) but rather with XML `bean`
definitions, annotated components (that is, classes annotated with `@Component`,
`@Controller`, and so forth), or `@Bean` methods in Java-based `@Configuration` classes.
These sources are then converted internally into instances of `BeanDefinition` and used to
load an entire Spring IoC container instance.
[[beans-constructor-vs-setter-injection]]
.Constructor-based or setter-based DI?
****
Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to
use constructors for mandatory dependencies and setter methods or configuration methods
for optional dependencies. Note that use of the xref:core/beans/annotation-config/autowired.adoc[@Autowired]
annotation on a setter method can be used to make the property be a required dependency;
however, constructor injection with programmatic validation of arguments is preferable.
The Spring team generally advocates constructor injection, as it lets you implement
application components as immutable objects and ensures that required dependencies
are not `null`. Furthermore, constructor-injected components are always returned to the client
(calling) code in a fully initialized state. As a side note, a large number of constructor
arguments is a bad code smell, implying that the class likely has too many
responsibilities and should be refactored to better address proper separation of concerns.
Setter injection should primarily only be used for optional dependencies that can be
assigned reasonable default values within the class. Otherwise, not-null checks must be
performed everywhere the code uses the dependency. One benefit of setter injection is that
setter methods make objects of that class amenable to reconfiguration or re-injection
later. Management through xref:integration/jmx.adoc[JMX MBeans] is therefore a compelling
use case for setter injection.
Use the DI style that makes the most sense for a particular class. Sometimes, when dealing
with third-party classes for which you do not have the source, the choice is made for you.
For example, if a third-party class does not expose any setter methods, then constructor
injection may be the only available form of DI.
****
[[beans-dependency-resolution]]
== Dependency Resolution Process
The container performs bean dependency resolution as follows:
* The `ApplicationContext` is created and initialized with configuration metadata that
describes all the beans. Configuration metadata can be specified by XML, Java code, or
annotations.
* For each bean, its dependencies are expressed in the form of properties, constructor
arguments, or arguments to the static-factory method (if you use that instead of a
normal constructor). These dependencies are provided to the bean, when the bean is
actually created.
* Each property or constructor argument is an actual definition of the value to set, or
a reference to another bean in the container.
* Each property or constructor argument that is a value is converted from its specified
format to the actual type of that property or constructor argument. By default, Spring
can convert a value supplied in string format to all built-in types, such as `int`,
`long`, `String`, `boolean`, and so forth.
The Spring container validates the configuration of each bean as the container is created.
However, the bean properties themselves are not set until the bean is actually created.
Beans that are singleton-scoped and set to be pre-instantiated (the default) are created
when the container is created. Scopes are defined in xref:core/beans/factory-scopes.adoc[Bean Scopes]. Otherwise,
the bean is created only when it is requested. Creation of a bean potentially causes a
graph of beans to be created, as the bean's dependencies and its dependencies'
dependencies (and so on) are created and assigned. Note that resolution mismatches among
those dependencies may show up late -- that is, on first creation of the affected bean.
.Circular dependencies
****
If you use predominantly constructor injection, it is possible to create an unresolvable
circular dependency scenario.
For example: Class A requires an instance of class B through constructor injection, and
class B requires an instance of class A through constructor injection. If you configure
beans for classes A and B to be injected into each other, the Spring IoC container
detects this circular reference at runtime, and throws a
`BeanCurrentlyInCreationException`.
One possible solution is to edit the source code of some classes to be configured by
setters rather than constructors. Alternatively, avoid constructor injection and use
setter injection only. In other words, although it is not recommended, you can configure
circular dependencies with setter injection.
Unlike the typical case (with no circular dependencies), a circular dependency
between bean A and bean B forces one of the beans to be injected into the other prior to
being fully initialized itself (a classic chicken-and-egg scenario).
****
You can generally trust Spring to do the right thing. It detects configuration problems,
such as references to non-existent beans and circular dependencies, at container
load-time. Spring sets properties and resolves dependencies as late as possible, when
the bean is actually created. This means that a Spring container that has loaded
correctly can later generate an exception when you request an object if there is a
problem creating that object or one of its dependencies -- for example, the bean throws an
exception as a result of a missing or invalid property. This potentially delayed
visibility of some configuration issues is why `ApplicationContext` implementations by
default pre-instantiate singleton beans. At the cost of some upfront time and memory to
create these beans before they are actually needed, you discover configuration issues
when the `ApplicationContext` is created, not later. You can still override this default
behavior so that singleton beans initialize lazily, rather than being eagerly
pre-instantiated.
If no circular dependencies exist, when one or more collaborating beans are being
injected into a dependent bean, each collaborating bean is totally configured prior
to being injected into the dependent bean. This means that, if bean A has a dependency on
bean B, the Spring IoC container completely configures bean B prior to invoking the
setter method on bean A. In other words, the bean is instantiated (if it is not a
pre-instantiated singleton), its dependencies are set, and the relevant lifecycle
methods (such as a xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-initializingbean[configured init method]
or the xref:core/beans/factory-nature.adoc#beans-factory-lifecycle-initializingbean[InitializingBean callback method])
are invoked.
[[beans-some-examples]]
== Examples of Dependency Injection
The following example uses XML-based configuration metadata for setter-based DI. A small
part of a Spring XML configuration file specifies some bean definitions as follows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
----
The following example shows the corresponding `ExampleBean` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class ExampleBean {
lateinit var beanOne: AnotherBean
lateinit var beanTwo: YetAnotherBean
var i: Int = 0
}
----
======
In the preceding example, setters are declared to match against the properties specified
in the XML file. The following example uses constructor-based DI:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="exampleBean" class="examples.ExampleBean">
<!-- constructor injection using the nested ref element -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>
<!-- constructor injection using the neater ref attribute -->
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg type="int" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
----
The following example shows the corresponding `ExampleBean` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public ExampleBean(
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
this.beanOne = anotherBean;
this.beanTwo = yetAnotherBean;
this.i = i;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class ExampleBean(
private val beanOne: AnotherBean,
private val beanTwo: YetAnotherBean,
private val i: Int)
----
======
The constructor arguments specified in the bean definition are used as arguments to
the constructor of the `ExampleBean`.
Now consider a variant of this example, where, instead of using a constructor, Spring is
told to call a `static` factory method to return an instance of the object:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
<constructor-arg ref="anotherExampleBean"/>
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
----
The following example shows the corresponding `ExampleBean` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class ExampleBean {
// a private constructor
private ExampleBean(...) {
...
}
// a static factory method; the arguments to this method can be
// considered the dependencies of the bean that is returned,
// regardless of how those arguments are actually used.
public static ExampleBean createInstance (
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
ExampleBean eb = new ExampleBean (...);
// some other operations...
return eb;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class ExampleBean private constructor() {
companion object {
// a static factory method; the arguments to this method can be
// considered the dependencies of the bean that is returned,
// regardless of how those arguments are actually used.
@JvmStatic
fun createInstance(anotherBean: AnotherBean, yetAnotherBean: YetAnotherBean, i: Int): ExampleBean {
val eb = ExampleBean (...)
// some other operations...
return eb
}
}
}
----
======
Arguments to the `static` factory method are supplied by `<constructor-arg/>` elements,
exactly the same as if a constructor had actually been used. The type of the class being
returned by the factory method does not have to be of the same type as the class that
contains the `static` factory method (although, in this example, it is). An instance
(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.
@@ -0,0 +1,40 @@
[[beans-factory-dependson]]
= Using `depends-on`
If a bean is a dependency of another bean, that usually means that one bean is set as a
property of another. Typically you accomplish this with the <<beans-ref-element, `<ref/>`
element>> in XML-based configuration metadata. However, sometimes dependencies between
beans are less direct. An example is when a static initializer in a class needs to be
triggered, such as for database driver registration. The `depends-on` attribute can
explicitly force one or more beans to be initialized before the bean using this element
is initialized. The following example uses the `depends-on` attribute to express a
dependency on a single bean:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
----
To express a dependency on multiple beans, supply a list of bean names as the value of
the `depends-on` attribute (commas, whitespace, and semicolons are valid
delimiters):
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
<property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
----
NOTE: The `depends-on` attribute can specify both an initialization-time dependency and,
in the case of xref:core/beans/factory-scopes.adoc#beans-factory-scopes-singleton[singleton] beans only, a corresponding
destruction-time dependency. Dependent beans that define a `depends-on` relationship
with a given bean are destroyed first, prior to the given bean itself being destroyed.
Thus, `depends-on` can also control shutdown order.
@@ -0,0 +1,42 @@
[[beans-factory-lazy-init]]
= Lazy-initialized Beans
By default, `ApplicationContext` implementations eagerly create and configure all
xref:core/beans/factory-scopes.adoc#beans-factory-scopes-singleton[singleton] beans as part of the initialization
process. Generally, this pre-instantiation is desirable, because errors in the
configuration or surrounding environment are discovered immediately, as opposed to hours
or even days later. When this behavior is not desirable, you can prevent
pre-instantiation of a singleton bean by marking the bean definition as being
lazy-initialized. A lazy-initialized bean tells the IoC container to create a bean
instance when it is first requested, rather than at startup.
In XML, this behavior is controlled by the `lazy-init` attribute on the `<bean/>`
element, as the following example shows:
[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 `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 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>
----
@@ -0,0 +1,434 @@
[[beans-factory-method-injection]]
= 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,
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]
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
shows this approach:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages",fold="none"]
----
package fiona.apple;
// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* A class that uses a stateful Command-style class to perform
* some processing.
*/
public class CommandManager implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Object process(Map commandState) {
// grab a new instance of the appropriate Command
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
protected Command createCommand() {
// notice the Spring API dependency!
return this.applicationContext.getBean("command", Command.class);
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages",fold="none"]
----
package fiona.apple
// Spring-API imports
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
// A class that uses a stateful Command-style class to perform
// some processing.
class CommandManager : ApplicationContextAware {
private lateinit var applicationContext: ApplicationContext
fun process(commandState: Map<*, *>): Any {
// grab a new instance of the appropriate Command
val command = createCommand()
// set the state on the (hopefully brand new) Command instance
command.state = commandState
return command.execute()
}
// notice the Spring API dependency!
protected fun createCommand() =
applicationContext.getBean("command", Command::class.java)
override fun setApplicationContext(applicationContext: ApplicationContext) {
this.applicationContext = applicationContext
}
}
----
======
The preceding is not desirable, because the business code is aware of and coupled to the
Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC
container, lets you handle this use case cleanly.
****
You can read more about the motivation for Method Injection in
https://spring.io/blog/2004/08/06/method-injection/[this blog entry].
****
[[beans-factory-lookup-method-injection]]
== Lookup Method Injection
Lookup method injection is the ability of the container to override methods on
container-managed beans and return the lookup result for another named bean in the
container. The lookup typically involves a prototype bean, as in the scenario described
in xref:core/beans/dependencies/factory-method-injection.adoc[the preceding section]. The Spring Framework
implements this method injection by using bytecode generation from the CGLIB library to
dynamically generate a subclass that overrides the method.
[NOTE]
====
* For this dynamic subclassing to work, the class that the Spring bean container
subclasses cannot be `final`, and the method to be overridden cannot be `final`, either.
* Unit-testing a class that has an `abstract` method requires you to subclass the class
yourself and to supply a stub implementation of the `abstract` method.
* Concrete methods are also necessary for component scanning, which requires concrete
classes to pick up.
* A further key limitation is that lookup methods do not work with factory methods and
in particular not with `@Bean` methods in configuration classes, since, in that case,
the container is not in charge of creating the instance and therefore cannot create
a runtime-generated subclass on the fly.
====
In the case of the `CommandManager` class in the previous code snippet, the
Spring container dynamically overrides the implementation of the `createCommand()`
method. The `CommandManager` class does not have any Spring dependencies, as
the reworked example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages",fold="none"]
----
package fiona.apple;
// no more Spring imports!
public abstract class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// okay... but where is the implementation of this method?
protected abstract Command createCommand();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages",fold="none"]
----
package fiona.apple
// no more Spring imports!
abstract class CommandManager {
fun process(commandState: Any): Any {
// grab a new instance of the appropriate Command interface
val command = createCommand()
// set the state on the (hopefully brand new) Command instance
command.state = commandState
return command.execute()
}
// okay... but where is the implementation of this method?
protected abstract fun createCommand(): Command
}
----
======
In the client class that contains the method to be injected (the `CommandManager` in this
case), the method to be injected requires a signature of the following form:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
----
If the method is `abstract`, the dynamically-generated subclass implements the method.
Otherwise, the dynamically-generated subclass overrides the concrete method defined in
the original class. Consider the following example:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- inject dependencies here as required -->
</bean>
<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>
----
The bean identified as `commandManager` calls its own `createCommand()` method
whenever it needs a new instance of the `myCommand` bean. You must be careful to deploy
the `myCommand` bean as a prototype if that is actually what is needed. If it is
a xref:core/beans/factory-scopes.adoc#beans-factory-scopes-singleton[singleton], the same instance of the `myCommand`
bean is returned each time.
Alternatively, within the annotation-based component model, you can declare a lookup
method through the `@Lookup` annotation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup("myCommand")
protected abstract Command createCommand();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
abstract class CommandManager {
fun process(commandState: Any): Any {
val command = createCommand()
command.state = commandState
return command.execute()
}
@Lookup("myCommand")
protected abstract fun createCommand(): Command
}
----
======
Or, more idiomatically, you can rely on the target bean getting resolved against the
declared return type of the lookup method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup
protected abstract Command createCommand();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
abstract class CommandManager {
fun process(commandState: Any): Any {
val command = createCommand()
command.state = commandState
return command.execute()
}
@Lookup
protected abstract fun createCommand(): Command
}
----
======
Note that you should typically declare such annotated lookup methods with a concrete
stub implementation, in order for them to be compatible with Spring's component
scanning rules where abstract classes get ignored by default. This limitation does not
apply to explicitly registered or explicitly imported bean classes.
[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].
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
A less useful form of method injection than lookup method injection is the ability to
replace arbitrary methods in a managed bean with another method implementation. You
can safely skip the rest of this section until you actually need this functionality.
With XML-based configuration metadata, you can use the `replaced-method` element to
replace an existing method implementation with another, for a deployed bean. Consider
the following class, which has a method called `computeValue` that we want to override:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
public class MyValueCalculator {
public String computeValue(String input) {
// some real code...
}
// some other methods...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class MyValueCalculator {
fun computeValue(input: String): String {
// some real code...
}
// some other methods...
}
----
======
A class that implements the `org.springframework.beans.factory.support.MethodReplacer`
interface provides the new method definition, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
/**
* meant to be used to override the existing computeValue(String)
* implementation in MyValueCalculator
*/
public class ReplacementComputeValue implements MethodReplacer {
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// get the input value, work with it, and return a computed result
String input = (String) args[0];
...
return ...;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
/**
* meant to be used to override the existing computeValue(String)
* implementation in MyValueCalculator
*/
class ReplacementComputeValue : MethodReplacer {
override fun reimplement(obj: Any, method: Method, args: Array<out Any>): Any {
// get the input value, work with it, and return a computed result
val input = args[0] as String;
...
return ...;
}
}
----
======
The bean definition to deploy the original class and specify the method override would
resemble the following example:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
<!-- arbitrary method replacement -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
----
You can use one or more `<arg-type/>` elements within the `<replaced-method/>`
element to indicate the method signature of the method being overridden. The signature
for the arguments is necessary only if the method is overloaded and multiple variants
exist within the class. For convenience, the type string for an argument may be a
substring of the fully qualified type name. For example, the following all match
`java.lang.String`:
[source,java,indent=0,subs="verbatim,quotes"]
----
java.lang.String
String
Str
----
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.

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