Compare commits

...

347 Commits

Author SHA1 Message Date
Spring Builds da19cb342d Release v6.0.7 2023-03-20 09:37:53 +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
1308 changed files with 16433 additions and 11486 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ spring-test/test-output/
# Maven artifacts
pom.xml
target/
/target/
# Eclipse artifacts, including WTP generated manifests
bin
+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.6-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
+7 -9
View File
@@ -1,5 +1,5 @@
plugins {
id 'io.spring.nohttp' version '0.0.10'
id 'io.spring.nohttp' version '0.0.11'
id 'io.freefair.aspectj' version '6.5.0.3' apply false
// kotlinVersion is managed in gradle.properties
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
@@ -78,7 +78,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "10.6.0"
toolVersion = "10.9.1"
configDirectory.set(rootProject.file("src/checkstyle"))
}
@@ -121,16 +121,14 @@ configure([rootProject] + javaProjects) { project ->
"https://docs.jboss.org/jbossas/javadoc/7.1.2.Final/",
"https://www.eclipse.org/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.
//
// warning: The code being documented uses packages in the unnamed module, but the packages defined in https://junit.org/junit5/docs/5.9.2/api/ are in named modules.
// TODO Uncomment link to JUnit 5 docs once we execute Gradle with Java 18+.
// See https://github.com/spring-projects/spring-framework/issues/27497
//
// "https://junit.org/junit5/docs/5.9.2/api/",
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
+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"]
-1
View File
@@ -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 PATH $JAVA_HOME/bin:$PATH
+2 -5
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"
;;
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"
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.6+10/bellsoft-jdk17.0.6+10-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"
echo "https://github.com/bell-sw/Liberica/releases/download/19.0.2+9/bellsoft-jdk19.0.2+9-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 java19
do
JDK_URL=$( /get-jdk-url.sh $jdk )
mkdir $jdk
-3
View File
@@ -8,7 +8,4 @@ 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
+14 -44
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,37 @@ 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:
<<: *docker-resource-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 +95,7 @@ resources:
source:
<<: *docker-resource-source
repository: ((docker-hub-organization))/spring-framework-ci
tag: ((milestone))
- name: every-morning
type: time
icon: alarm
@@ -125,14 +127,6 @@ resources:
access_token: ((github-ci-status-token))
branch: ((branch))
context: build
- name: repo-status-jdk18-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
@@ -176,7 +170,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,34 +231,6 @@ jobs:
"zip.type": "schema"
get_params:
threads: 8
- name: jdk18-build
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk18-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: 18
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk18-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk18-build
params: { state: "success", commit: "git-repo" }
- name: jdk19-build
serial: true
public: true
@@ -365,6 +331,7 @@ jobs:
params:
RELEASE_TYPE: M
<<: *github-task-params
<<: *docker-resource-source
- put: github-pre-release
params:
<<: *changelog-task-params
@@ -403,6 +370,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 +415,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 +433,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,7 +441,7 @@ jobs:
groups:
- name: "builds"
jobs: ["build", "jdk18-build", "jdk19-build"]
jobs: ["build", "jdk19-build"]
- name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
+1 -1
View File
@@ -4,6 +4,6 @@ set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK18 \
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK19 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
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
+1 -7
View File
@@ -28,13 +28,7 @@ javadoc {
}
dependencies {
asciidoctorExtensions "io.spring.asciidoctor.backends:spring-asciidoctor-backends:0.0.3"
}
repositories {
maven {
url "https://repo.spring.io/release"
}
asciidoctorExtensions "io.spring.asciidoctor.backends:spring-asciidoctor-backends:0.0.5"
}
/**
@@ -0,0 +1,9 @@
aot=core.aot
aot-basics=core.aot.basics
aot-refresh=core.aot.refresh
aot-bean-factory-initialization-contributions=core.aot.bean-factory-initialization-contributions
aot-bean-registration-contributions=core.aot.bean-registration-contributions
aot-hints=core.aot.hints
aot-hints-import-runtime-hints=core.aot.hints.import-runtime-hints
aot-hints-reflective=core.aot.hints.reflective
aot-hints-register-reflection-for-binding=core.aot.hints.register-reflection-for-binding
@@ -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
@@ -1100,12 +1100,15 @@ 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` methods cannot be advised, as they cannot be overridden.
* There is no need to add CGLIB to your classpath. As of Spring 3.2, 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.
* `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.
There is little performance difference between CGLIB proxying and dynamic proxies.
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.
File diff suppressed because it is too large Load Diff
@@ -16,7 +16,9 @@ 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).
* The return type of methods annotated with `@Bean` should be the most specific type possible (typically the concrete class, not an interface) in order to support proper type inference without invoking the corresponding `@Bean` method at build time.
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:
@@ -123,7 +123,7 @@ easy to do in Spring. You do not actually have to do anything or know anything a
the Spring internals (or even about classes such as the `FieldRetrievingFactoryBean`).
The following example enumeration shows how easy injecting an enum value is:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package jakarta.persistence;
@@ -134,7 +134,7 @@ The following example enumeration shows how easy injecting an enum value is:
EXTENDED
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package jakarta.persistence
@@ -148,7 +148,7 @@ The following example enumeration shows how easy injecting an enum value is:
Now consider the following setter of type `PersistenceContextType` and the corresponding bean definition:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package example;
@@ -162,7 +162,7 @@ Now consider the following setter of type `PersistenceContextType` and the corre
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package example
@@ -796,7 +796,7 @@ element results in a single `SimpleDateFormat` bean definition). Spring features
number of convenience classes that support this scenario. In the following example, we
use the `NamespaceHandlerSupport` class:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package org.springframework.samples.xml;
@@ -810,7 +810,7 @@ use the `NamespaceHandlerSupport` class:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package org.springframework.samples.xml
@@ -847,7 +847,7 @@ responsible for parsing one distinct top-level XML element defined in the schema
the parser, we' have access to the XML element (and thus to its subelements, too) so that
we can parse our custom XML content, as you can see in the following example:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package org.springframework.samples.xml;
@@ -884,7 +884,7 @@ the basic grunt work of creating a single `BeanDefinition`.
<2> We supply the `AbstractSingleBeanDefinitionParser` superclass with the type that our
single `BeanDefinition` represents.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package org.springframework.samples.xml
@@ -1056,7 +1056,7 @@ setter method for the `components` property. This makes it hard (or rather impos
to configure a bean definition for the `Component` class by using setter injection.
The following listing shows the `Component` class:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package com.foo;
@@ -1087,7 +1087,7 @@ The following listing shows the `Component` class:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package com.foo
@@ -1114,7 +1114,7 @@ The typical solution to this issue is to create a custom `FactoryBean` that expo
setter property for the `components` property. The following listing shows such a custom
`FactoryBean`:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package com.foo;
@@ -1154,7 +1154,7 @@ setter property for the `components` property. The following listing shows such
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package com.foo
@@ -1226,7 +1226,7 @@ listing shows:
Again following <<core.appendix.xsd-custom-introduction, the process described earlier>>,
we then create a custom `NamespaceHandler`:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package com.foo;
@@ -1240,7 +1240,7 @@ we then create a custom `NamespaceHandler`:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package com.foo
@@ -1259,7 +1259,7 @@ Next up is the custom `BeanDefinitionParser`. Remember that we are creating
a `BeanDefinition` that describes a `ComponentFactoryBean`. The following
listing shows our custom `BeanDefinitionParser` implementation:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package com.foo;
@@ -1308,7 +1308,7 @@ listing shows our custom `BeanDefinitionParser` implementation:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package com.foo
@@ -1403,14 +1403,14 @@ the named JCache for us. We can also modify the existing `BeanDefinition` for th
`'checkingAccountService'` so that it has a dependency on this new
JCache-initializing `BeanDefinition`. The following listing shows our `JCacheInitializer`:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package com.foo;
public class JCacheInitializer {
private String name;
private final String name;
public JCacheInitializer(String name) {
this.name = name;
@@ -1421,7 +1421,7 @@ JCache-initializing `BeanDefinition`. The following listing shows our `JCacheIni
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package com.foo
@@ -1453,7 +1453,7 @@ the XSD schema that describes the custom attribute, as follows:
Next, we need to create the associated `NamespaceHandler`, as follows:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package com.foo;
@@ -1469,7 +1469,7 @@ Next, we need to create the associated `NamespaceHandler`, as follows:
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package com.foo
@@ -1490,7 +1490,7 @@ Next, we need to create the parser. Note that, in this case, because we are goin
an XML attribute, we write a `BeanDefinitionDecorator` rather than a `BeanDefinitionParser`.
The following listing shows our `BeanDefinitionDecorator` implementation:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package com.foo;
@@ -1544,7 +1544,7 @@ The following listing shows our `BeanDefinitionDecorator` implementation:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package com.foo
@@ -524,15 +524,13 @@ container that hosts the bean. A bean usually has only one identifier. However,
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 the 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. As a
historical note, in versions prior to Spring 3.1, the `id` attribute was
defined as an `xsd:ID` type, which constrained possible characters. As of 3.1,
it is defined as an `xsd:string` type. Note that bean `id` uniqueness is still
enforced by the container, though no longer by XML parsers.
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,
@@ -951,7 +949,7 @@ order in which the constructor arguments are defined in a bean definition is the
in which those arguments are supplied to the appropriate constructor when the bean is
being instantiated. Consider the following class:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package x.y;
@@ -963,7 +961,7 @@ being instantiated. Consider the following class:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package x.y
@@ -995,7 +993,7 @@ 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:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package examples;
@@ -1014,7 +1012,7 @@ by type without help. Consider the following class:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package examples
@@ -1079,7 +1077,7 @@ https://download.oracle.com/javase/8/docs/api/java/beans/ConstructorProperties.h
JDK annotation to explicitly name your constructor arguments. The sample class would
then have to look as follows:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package examples;
@@ -1095,7 +1093,7 @@ then have to look as follows:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package examples
@@ -2278,10 +2276,9 @@ and by <<beans-factory-client,making a `getBean("B")` call to the container>> as
typically new) bean B instance every time bean A needs it. The following example
shows this approach:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages",fold="none"]
.Java
----
// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;
// Spring-API imports
@@ -2289,6 +2286,10 @@ shows this approach:
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;
@@ -2312,16 +2313,17 @@ shows this approach:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages",fold="none"]
.Kotlin
----
// a class that uses a stateful Command-style class to perform some processing
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
@@ -2384,7 +2386,7 @@ Spring container dynamically overrides the implementation of the `createCommand(
method. The `CommandManager` class does not have any Spring dependencies, as
the reworked example shows:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages",fold="none"]
.Java
----
package fiona.apple;
@@ -2405,7 +2407,7 @@ the reworked example shows:
protected abstract Command createCommand();
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages",fold="none"]
.Kotlin
----
package fiona.apple
@@ -2704,8 +2706,8 @@ The following table describes the supported scopes:
the context of a web-aware Spring `ApplicationContext`.
|===
NOTE: As of Spring 3.0, a thread scope is available but is not registered by default. For
more information, see the documentation for
NOTE: A thread scope is available but is not registered by default. For more information,
see the documentation for
{api-spring-framework}/context/support/SimpleThreadScope.html[`SimpleThreadScope`].
For instructions on how to register this or any other custom scope, see
<<beans-factory-scopes-custom-using>>.
@@ -4216,7 +4218,7 @@ it is created by the container and prints the resulting string to the system con
The following listing shows the custom `BeanPostProcessor` implementation class definition:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package scripting;
@@ -4236,9 +4238,11 @@ The following listing shows the custom `BeanPostProcessor` implementation class
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package scripting
import org.springframework.beans.factory.config.BeanPostProcessor
class InstantiationTracingBeanPostProcessor : BeanPostProcessor {
@@ -4590,26 +4594,24 @@ 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 <<beans-java, 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 the
https://spring.io/tools[Spring Tools for Eclipse].
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 the bytecode metadata for wiring up components instead of angle-bracket 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 <<beans-factory-extension-bpp-examples-aabpp>>, using
a `BeanPostProcessor` in conjunction with annotations is a common means of extending the
Spring IoC container. For example, Spring 2.5 introduced an annotation-based approach to
drive Spring's dependency injection. Essentially, the <<beans-autowired-annotation,
`@Autowired`>> annotation provides the same capabilities as described in
<<beans-factory-autowire>> but with more fine-grained control and wider applicability.
Spring 2.5 also added support for JSR-250 annotations, such as `@PostConstruct` and
`@PreDestroy`. Spring 3.0 added 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 <<beans-standard-annotations,
relevant section>>.
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 <<beans-factory-extension-bpp-examples-aabpp>>, using a
`BeanPostProcessor` in conjunction with annotations is a common means of extending the
Spring IoC container. For example, the <<beans-autowired-annotation, `@Autowired`>>
annotation provides the same capabilities as described in <<beans-factory-autowire>> 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 <<beans-standard-annotations, relevant section>>.
[NOTE]
====
@@ -6237,7 +6239,7 @@ simply to be added to the application's classpath like any other library.
Most examples in this chapter use XML to specify the configuration metadata that produces
each `BeanDefinition` within the Spring container. The previous section
(<<beans-annotation-config>>) demonstrates how to provide a lot of the configuration
metadata through source-level annotations. Even in those examples, however, the "`base`"
metadata through source-level annotations. Even in those examples, however, the "base"
bean definitions are explicitly defined in the XML file, while the annotations drive only
the dependency injection. This section describes an option for implicitly detecting the
candidate components by scanning the classpath. Candidate components are classes that
@@ -6249,10 +6251,9 @@ the container.
[NOTE]
====
Starting with Spring 3.0, many features provided by the Spring JavaConfig project are
part of the core Spring Framework. This allows you to define beans using Java rather
than using the traditional XML files. Take a look at the `@Configuration`, `@Bean`,
`@Import`, and `@DependsOn` annotations for examples of how to use these new features.
You can define beans using Java rather than using XML files. Take a look at the
`@Configuration`, `@Bean`, `@Import`, and `@DependsOn` annotations for examples of how to
use these features.
====
@@ -7172,15 +7173,15 @@ arrangement (as though no index were present at all) by setting `spring.index.ig
[[beans-standard-annotations]]
== Using JSR 330 Standard Annotations
Starting with Spring 3.0, Spring offers support for JSR-330 standard annotations
(Dependency Injection). Those annotations are scanned in the same way as the Spring
annotations. To use them, you need to have the relevant jars in your classpath.
Spring offers support for JSR-330 standard annotations (Dependency Injection). Those
annotations are scanned in the same way as the Spring annotations. To use them, you need
to have the relevant jars in your classpath.
[NOTE]
=====
If you use Maven, the `jakarta.inject` artifact is available in the standard Maven
repository (
https://repo1.maven.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/[https://repo1.maven.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/]).
https://repo.maven.apache.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/[https://repo.maven.apache.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/]).
You can add the following dependency to your file pom.xml:
[source,xml,indent=0,subs="verbatim,quotes"]
@@ -7562,7 +7563,7 @@ The simplest possible `@Configuration` class reads as follows:
public class AppConfig {
@Bean
public MyService myService() {
public MyServiceImpl myService() {
return new MyServiceImpl();
}
}
@@ -7574,7 +7575,7 @@ The simplest possible `@Configuration` class reads as follows:
class AppConfig {
@Bean
fun myService(): MyService {
fun myService(): MyServiceImpl {
return MyServiceImpl()
}
}
@@ -8611,22 +8612,24 @@ about singletons here.
[NOTE]
====
As of Spring 3.2, it is no longer necessary to add CGLIB to your classpath because CGLIB
classes have been repackaged under `org.springframework.cglib` and included directly
within the spring-core JAR.
It is not necessary to add CGLIB to your classpath because CGLIB classes are repackaged
under the `org.springframework.cglib` package and included directly within the
`spring-core` JAR.
====
[TIP]
====
There are a few restrictions due to the fact that CGLIB dynamically adds features at
startup-time. In particular, configuration classes must not be final. However, as
of 4.3, any constructors are allowed on configuration classes, including the use of
`@Autowired` or a single non-default constructor declaration for default injection.
startup-time. In particular, configuration classes must not be final. However, any
constructors are allowed on configuration classes, including the use of `@Autowired` or a
single non-default constructor declaration for default injection.
If you prefer to avoid any CGLIB-imposed limitations, consider declaring your `@Bean`
methods on non-`@Configuration` classes (for example, on plain `@Component` classes instead).
Cross-method calls between `@Bean` methods are not then intercepted, so you have
to exclusively rely on dependency injection at the constructor or method level there.
methods on non-`@Configuration` classes (for example, on plain `@Component` classes
instead) or by annotating your configuration class with
`@Configuration(proxyBeanMethods = false)`. Cross-method calls between `@Bean` methods
are then not intercepted, so you have to exclusively rely on dependency injection at the
constructor or method level there.
====
@@ -1978,7 +1978,7 @@ The definition of `TemplateParserContext` follows:
This section lists the classes used in the examples throughout this chapter.
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Inventor.Java
----
package org.spring.samples.spel.inventor;
@@ -2051,18 +2051,20 @@ This section lists the classes used in the examples throughout this chapter.
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Inventor.kt
----
class Inventor(
var name: String,
var nationality: String,
var inventions: Array<String>? = null,
var birthdate: Date = GregorianCalendar().time,
var placeOfBirth: PlaceOfBirth? = null)
package org.spring.samples.spel.inventor
class Inventor(
var name: String,
var nationality: String,
var inventions: Array<String>? = null,
var birthdate: Date = GregorianCalendar().time,
var placeOfBirth: PlaceOfBirth? = null)
----
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.PlaceOfBirth.java
----
package org.spring.samples.spel.inventor;
@@ -2098,13 +2100,15 @@ class Inventor(
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.PlaceOfBirth.kt
----
package org.spring.samples.spel.inventor
class PlaceOfBirth(var city: String, var country: String? = null) {
----
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Society.java
----
package org.spring.samples.spel.inventor;
@@ -2147,7 +2151,7 @@ class Inventor(
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Society.kt
----
package org.spring.samples.spel.inventor
@@ -2,7 +2,7 @@
= Validation, Data Binding, and Type Conversion
There are pros and cons for considering validation as business logic, and Spring offers
a design for validation (and data binding) that does not exclude either one of them.
a design for validation and data binding that does not exclude either one of them.
Specifically, validation should not be tied to the web tier and should be easy to localize,
and it should be possible to plug in any available validator. Considering these concerns,
Spring provides a `Validator` contract that is both basic and eminently usable
@@ -15,18 +15,18 @@ provides the aptly named `DataBinder` to do exactly that. The `Validator` and th
limited to the web layer.
The `BeanWrapper` is a fundamental concept in the Spring Framework and is used in a lot
of places. However, you probably do not need to use the `BeanWrapper`
directly. Because this is reference documentation, however, we felt that some explanation
might be in order. We explain the `BeanWrapper` in this chapter, since, if you are
going to use it at all, you are most likely do so when trying to bind data to objects.
of places. However, you probably do not need to use the `BeanWrapper` directly. Because
this is reference documentation, however, we feel that some explanation might be in
order. We explain the `BeanWrapper` in this chapter, since, if you are going to use it at
all, you are most likely do so when trying to bind data to objects.
Spring's `DataBinder` and the lower-level `BeanWrapper` both use `PropertyEditorSupport`
implementations to parse and format property values. The `PropertyEditor` and
`PropertyEditorSupport` types are part of the JavaBeans specification and are also
explained in this chapter. Spring 3 introduced a `core.convert` package that provides a
general type conversion facility, as well as a higher-level "`format`" package for
formatting UI field values. You can use these packages as simpler alternatives to
`PropertyEditorSupport` implementations. They are also discussed in this chapter.
explained in this chapter. Spring's `core.convert` package provides a general type
conversion facility, as well as a higher-level `format` package for formatting UI field
values. You can use these packages as simpler alternatives to `PropertyEditorSupport`
implementations. They are also discussed in this chapter.
Spring supports Java Bean Validation through setup infrastructure and an adaptor to
Spring's own `Validator` contract. Applications can enable Bean Validation once globally,
@@ -640,7 +640,7 @@ support for additional `PropertyEditor` instances to an `ApplicationContext`.
Consider the following example, which defines a user class called `ExoticType` and
another class called `DependsOnExoticType`, which needs `ExoticType` set as a property:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package example;
@@ -663,7 +663,7 @@ another class called `DependsOnExoticType`, which needs `ExoticType` set as a pr
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package example
@@ -689,12 +689,14 @@ string, which a `PropertyEditor` converts into an actual
The `PropertyEditor` implementation could look similar to the following:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
// converts string representation to ExoticType object
package example;
import java.beans.PropertyEditorSupport;
// converts string representation to ExoticType object
public class ExoticTypeEditor extends PropertyEditorSupport {
public void setAsText(String text) {
@@ -702,14 +704,14 @@ The `PropertyEditor` implementation could look similar to the following:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
// converts string representation to ExoticType object
package example
import java.beans.PropertyEditorSupport
// converts string representation to ExoticType object
class ExoticTypeEditor : PropertyEditorSupport() {
override fun setAsText(text: String) {
@@ -752,7 +754,7 @@ instances for each bean creation attempt.
The following example shows how to create your own `PropertyEditorRegistrar` implementation:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package com.foo.editors.spring;
@@ -768,7 +770,7 @@ The following example shows how to create your own `PropertyEditorRegistrar` imp
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package com.foo.editors.spring
@@ -861,12 +863,12 @@ as needed.
[[core-convert]]
== Spring Type Conversion
Spring 3 introduced a `core.convert` package that provides a general type conversion
system. The system defines an SPI to implement type conversion logic and an API
to perform type conversions at runtime. Within a Spring container, you can use this system
as an alternative to `PropertyEditor` implementations to convert externalized bean property value
strings to the required property types. You can also use the public API anywhere in your
application where type conversion is needed.
The `core.convert` package provides a general type conversion system. The system defines
an SPI to implement type conversion logic and an API to perform type conversions at
runtime. Within a Spring container, you can use this system as an alternative to
`PropertyEditor` implementations to convert externalized bean property value strings to
the required property types. You can also use the public API anywhere in your application
where type conversion is needed.
@@ -876,7 +878,7 @@ application where type conversion is needed.
The SPI to implement type conversion logic is simple and strongly typed, as the following
interface definition shows:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.core.convert.converter;
@@ -901,7 +903,7 @@ Several converter implementations are provided in the `core.convert.support` pac
a convenience. These include converters from strings to numbers and other common types.
The following listing shows the `StringToInteger` class, which is a typical `Converter` implementation:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.core.convert.support;
@@ -922,7 +924,7 @@ When you need to centralize the conversion logic for an entire class hierarchy
(for example, when converting from `String` to `Enum` objects), you can implement
`ConverterFactory`, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.core.convert.converter;
@@ -938,7 +940,7 @@ where T is a subclass of R.
Consider the `StringToEnumConverterFactory` as an example:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.core.convert.support;
@@ -975,7 +977,7 @@ context that you can use when you implement your conversion logic. Such context
type conversion be driven by a field annotation or by generic information declared on a
field signature. The following listing shows the interface definition of `GenericConverter`:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.core.convert.converter;
@@ -1039,7 +1041,7 @@ might match only if the target entity type declares a static finder method (for
`ConversionService` defines a unified API for executing type conversion logic at
runtime. Converters are often run behind the following facade interface:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.core.convert;
@@ -1205,8 +1207,9 @@ web or desktop application. In such environments, you typically convert from `St
to support the client postback process, as well as back to `String` to support the
view rendering process. In addition, you often need to localize `String` values. The more
general `core.convert` `Converter` SPI does not address such formatting requirements
directly. To directly address them, Spring 3 introduced a convenient `Formatter` SPI that
provides a simple and robust alternative to `PropertyEditor` implementations for client environments.
directly. To directly address them, Spring provides a convenient `Formatter` SPI that
provides a simple and robust alternative to `PropertyEditor` implementations for client
environments.
In general, you can use the `Converter` SPI when you need to implement general-purpose type
conversion logic -- for example, for converting between a `java.util.Date` and a `Long`.
@@ -1222,7 +1225,7 @@ provides a unified type conversion API for both SPIs.
The `Formatter` SPI to implement field formatting logic is simple and strongly typed. The
following listing shows the `Formatter` interface definition:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.format;
@@ -1267,7 +1270,7 @@ a `java.text.DateFormat`.
The following `DateFormatter` is an example `Formatter` implementation:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package org.springframework.format.datetime;
@@ -1301,7 +1304,7 @@ The following `DateFormatter` is an example `Formatter` implementation:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
class DateFormatter(private val pattern: String) : Formatter<Date> {
@@ -1333,7 +1336,7 @@ Field formatting can be configured by field type or annotation. To bind
an annotation to a `Formatter`, implement `AnnotationFormatterFactory`. The following
listing shows the definition of the `AnnotationFormatterFactory` interface:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.format;
@@ -1349,15 +1352,14 @@ listing shows the definition of the `AnnotationFormatterFactory` interface:
To create an implementation:
. Parameterize A to be the field `annotationType` with which you wish to associate
. Parameterize `A` to be the field `annotationType` with which you wish to associate
formatting logic -- for example `org.springframework.format.annotation.DateTimeFormat`.
. Have `getFieldTypes()` return the types of fields on which the annotation can be used.
. Have `getPrinter()` return a `Printer` to print the value of an annotated field.
. Have `getParser()` return a `Parser` to parse a `clientValue` for an annotated field.
The following example `AnnotationFormatterFactory` implementation binds the `@NumberFormat`
annotation to a formatter to let a number style or pattern be
specified:
annotation to a formatter to let a number style or pattern be specified:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
@@ -1365,10 +1367,12 @@ specified:
public final class NumberFormatAnnotationFormatterFactory
implements AnnotationFormatterFactory<NumberFormat> {
private static final Set<Class<?>> FIELD_TYPES = Set.of(Short.class,
Integer.class, Long.class, Float.class, Double.class,
BigDecimal.class, BigInteger.class);
public Set<Class<?>> getFieldTypes() {
return new HashSet<Class<?>>(asList(new Class<?>[] {
Short.class, Integer.class, Long.class, Float.class,
Double.class, BigDecimal.class, BigInteger.class }));
return FIELD_TYPES;
}
public Printer<Number> getPrinter(NumberFormat annotation, Class<?> fieldType) {
@@ -1382,16 +1386,13 @@ specified:
private Formatter<Number> configureFormatterFrom(NumberFormat annotation, Class<?> fieldType) {
if (!annotation.pattern().isEmpty()) {
return new NumberStyleFormatter(annotation.pattern());
} else {
Style style = annotation.style();
if (style == Style.PERCENT) {
return new PercentStyleFormatter();
} else if (style == Style.CURRENCY) {
return new CurrencyStyleFormatter();
} else {
return new NumberStyleFormatter();
}
}
// else
return switch(annotation.style()) {
case Style.PERCENT -> new PercentStyleFormatter();
case Style.CURRENCY -> new CurrencyStyleFormatter();
default -> new NumberStyleFormatter();
};
}
}
----
@@ -1427,7 +1428,7 @@ specified:
}
----
To trigger formatting, you can annotate fields with @NumberFormat, as the following
To trigger formatting, you can annotate fields with `@NumberFormat`, as the following
example shows:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
@@ -1489,7 +1490,7 @@ for use with Spring's `DataBinder` and the Spring Expression Language (SpEL).
The following listing shows the `FormatterRegistry` SPI:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.format;
@@ -1525,7 +1526,7 @@ these rules once, and they are applied whenever formatting is needed.
`FormatterRegistrar` is an SPI for registering formatters and converters through the
FormatterRegistry. The following listing shows its interface definition:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.format;
@@ -1577,20 +1578,22 @@ For example, the following Java configuration registers a global `yyyyMMdd` form
public FormattingConversionService conversionService() {
// Use the DefaultFormattingConversionService but do not register defaults
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false);
DefaultFormattingConversionService conversionService =
new DefaultFormattingConversionService(false);
// Ensure @NumberFormat is still supported
conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
conversionService.addFormatterForFieldAnnotation(
new NumberFormatAnnotationFormatterFactory());
// Register JSR-310 date conversion with a specific global format
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"));
registrar.registerFormatters(conversionService);
DateTimeFormatterRegistrar dateTimeRegistrar = new DateTimeFormatterRegistrar();
dateTimeRegistrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"));
dateTimeRegistrar.registerFormatters(conversionService);
// Register date conversion with a specific global format
DateFormatterRegistrar registrar = new DateFormatterRegistrar();
registrar.setFormatter(new DateFormatter("yyyyMMdd"));
registrar.registerFormatters(conversionService);
DateFormatterRegistrar dateRegistrar = new DateFormatterRegistrar();
dateRegistrar.setFormatter(new DateFormatter("yyyyMMdd"));
dateRegistrar.registerFormatters(conversionService);
return conversionService;
}
@@ -1611,14 +1614,14 @@ For example, the following Java configuration registers a global `yyyyMMdd` form
addFormatterForFieldAnnotation(NumberFormatAnnotationFormatterFactory())
// Register JSR-310 date conversion with a specific global format
val registrar = DateTimeFormatterRegistrar()
registrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"))
registrar.registerFormatters(this)
val dateTimeRegistrar = DateTimeFormatterRegistrar()
dateTimeRegistrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyyMMdd"))
dateTimeRegistrar.registerFormatters(this)
// Register date conversion with a specific global format
val registrar = DateFormatterRegistrar()
registrar.setFormatter(DateFormatter("yyyyMMdd"))
registrar.registerFormatters(this)
val dateRegistrar = DateFormatterRegistrar()
dateRegistrar.setFormatter(DateFormatter("yyyyMMdd"))
dateRegistrar.registerFormatters(this)
}
}
}
@@ -1956,9 +1959,9 @@ javadoc for more information on these options.
[[validation-binder]]
=== Configuring a `DataBinder`
Since Spring 3, you can configure a `DataBinder` instance with a `Validator`. Once
configured, you can invoke the `Validator` by calling `binder.validate()`. Any validation
`Errors` are automatically added to the binder's `BindingResult`.
You can configure a `DataBinder` instance with a `Validator`. Once configured, you can
invoke the `Validator` by calling `binder.validate()`. Any validation `Errors` are
automatically added to the binder's `BindingResult`.
The following example shows how to use a `DataBinder` programmatically to invoke validation
logic after binding to a target object:
@@ -622,7 +622,7 @@ transactions being created and then rolled back in response to the
`UnsupportedOperationException` instance. The following listing shows the `FooService`
interface:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
// the service interface that we want to make transactional
@@ -641,7 +641,7 @@ interface:
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
// the service interface that we want to make transactional
@@ -662,7 +662,7 @@ interface:
The following example shows an implementation of the preceding interface:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package x.y.service;
@@ -690,7 +690,7 @@ The following example shows an implementation of the preceding interface:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package x.y.service
@@ -893,7 +893,7 @@ return type is reactive.
The following listing shows a modified version of the previously used `FooService`, but
this time the code uses reactive types:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
// the reactive service interface that we want to make transactional
@@ -912,7 +912,7 @@ this time the code uses reactive types:
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
// the reactive service interface that we want to make transactional
@@ -933,7 +933,7 @@ this time the code uses reactive types:
The following example shows an implementation of the preceding interface:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package x.y.service;
@@ -961,7 +961,7 @@ The following example shows an implementation of the preceding interface:
}
}
----
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary",chomp="-packages"]
.Kotlin
----
package x.y.service
@@ -2028,7 +2028,7 @@ configuration and AOP in general.
The following code shows the simple profiling aspect discussed earlier:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
[source,java,indent=0,subs="verbatim,quotes",role="primary",chomp="-packages"]
.Java
----
package x.y;
@@ -2065,9 +2065,15 @@ The following code shows the simple profiling aspect discussed earlier:
}
}
----
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
[source,kotlin,indent=0,subs="verbatim",role="secondary",chomp="-packages"]
.Kotlin
----
package x.y
import org.aspectj.lang.ProceedingJoinPoint
import org.springframework.util.StopWatch
import org.springframework.core.Ordered
class SimpleProfiler : Ordered {
private var order: Int = 0
+1 -1
View File
@@ -31,7 +31,7 @@ Brannen, Ramnivas Laddad, Arjen Poutsma, Chris Beams, Tareq Abedrabbo, Andy Clem
Syer, Oliver Gierke, Rossen Stoyanchev, Phillip Webb, Rob Winch, Brian Clozel, Stephane
Nicoll, Sebastien Deleuze, Jay Bryant, Mark Paluch
Copyright © 2002 - 2022 VMware, Inc. All Rights Reserved.
Copyright © 2002 - 2023 VMware, Inc. All Rights Reserved.
Copies of this document may be made for your own use and for distribution to others,
provided that you do not charge any fee for such copies and further provided that each
@@ -582,7 +582,7 @@ a similar contract to the JMS `MessageListener` interface but also gives the mes
method access to the JMS `Session` from which the `Message` was received.
The following listing shows the definition of the `SessionAwareMessageListener` interface:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.jms.listener;
@@ -31,7 +31,7 @@ The core class in Spring's JMX framework is the `MBeanExporter`. This class is
responsible for taking your Spring beans and registering them with a JMX `MBeanServer`.
For example, consider the following class:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages",chomp="-packages"]
----
package org.springframework.jmx;
@@ -358,7 +358,7 @@ an operation or an attribute.
The following example shows the annotated version of the `JmxTestBean` class that we
used in <<jmx-exporting-mbeanserver>>:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.jmx;
@@ -1067,7 +1067,7 @@ example, consider the scenario where one would like to be informed (through a
`Notification`) each and every time an attribute of a target MBean changes. The following
example writes notifications to the console:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package com.example;
@@ -1317,7 +1317,7 @@ published, and invoke the `sendNotification(Notification)` on the
In the following example, exported instances of the `JmxTestBean` publish a
`NotificationEvent` every time the `add(int, int)` operation is invoked:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.jmx;
@@ -9,6 +9,30 @@ Traces provide a holistic view of an entire system, crossing application boundar
Spring Framework instruments various parts of its own codebase to publish observations if an `ObservationRegistry` is configured.
You can learn more about {docs-spring-boot}/html/actuator.html#actuator.metrics[configuring the observability infrastructure in Spring Boot].
[[integration.observability.list]]
== List of produced Observations
Spring Framework instruments various features for observability.
As outlined <<integration.observability,at the beginning of this section>>, observations can generate timer Metrics and/or Traces depending on the configuration.
.Observations produced by Spring Framework
[%autowidth]
|===
|Observation name |Description
|<<integration.observability.http-client,`"http.client.requests"`>>
|Time spent for HTTP client exchanges
|<<integration.observability.http-server,`"http.server.requests"`>>
|Processing time for HTTP server exchanges at the Framework level
|===
NOTE: Observations are using Micrometer's official naming convention, but Metrics names will be automatically converted
https://micrometer.io/docs/concepts#_naming_meters[to the format preferred by the monitoring system backend]
(Prometheus, Atlas, Graphite, InfluxDB...).
[[integration.observability.concepts]]
== Micrometer Observation concepts
@@ -67,6 +91,12 @@ HTTP server exchanges observations are created with the name `"http.server.reque
Applications need to configure the `org.springframework.web.filter.ServerHttpObservationFilter` Servlet filter in their application.
It is using the `org.springframework.http.server.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`.
This will only record an observation as an error if the `Exception` has not been handled by the web Framework and has bubbled up to the Servlet filter.
Typically, all exceptions handled by Spring MVC's `@ExceptionHandler` and <<web.adoc#mvc-ann-rest-exceptions,`ProblemDetail` support>> will not be recorded with the observation.
You can, at any point during request processing, set the error field on the `ObservationContext` yourself:
include::code:UserController[]
By default, the following `KeyValues` are created:
.Low cardinality Keys
@@ -94,6 +124,12 @@ By default, the following `KeyValues` are created:
Applications need to configure the `org.springframework.web.filter.reactive.ServerHttpObservationFilter` reactive `WebFilter` in their application.
It is using the `org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention` by default, backed by the `ServerRequestObservationContext`.
This will only record an observation as an error if the `Exception` has not been handled by the web Framework and has bubbled up to the `WebFilter`.
Typically, all exceptions handled by Spring WebFlux's `@ExceptionHandler` and <<web.adoc#webflux-ann-rest-exceptions,`ProblemDetail` support>> will not be recorded with the observation.
You can, at any point during request processing, set the error field on the `ObservationContext` yourself:
include::code:UserController[]
By default, the following `KeyValues` are created:
.Low cardinality Keys
@@ -125,24 +161,27 @@ Unlike their server counterparts, the instrumentation is implemented directly in
[[integration.observability.http-client.resttemplate]]
=== RestTemplate
Applications must configure an `ObservationRegistry` on `RestTemplate` instances to enable the instrumentation; without that, observations are "no-ops".
Spring Boot will auto-configure `RestTemplateBuilder` beans with the observation registry already set.
Instrumentation is using the `org.springframework.http.client.observation.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
.Low cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`exception` _(required)_|Name of the exception thrown during the exchange, or `"none"` if no exception happened.
|`method` _(required)_|Name of HTTP request method or `"none"` if the request could not be created.
|`outcome` _(required)_|Outcome of the HTTP client exchange.
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided. Only the path part of the URI is considered.
|`client.name` _(required)_|Client name derived from the request URI host.
|`status` _(required)_|HTTP response raw status code, or `"IO_ERROR"` in case of `IOException`, or `"CLIENT_ERROR"` if no response was received.
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided.
|`outcome` _(required)_|Outcome of the HTTP client exchange.
|`exception` _(required)_|Name of the exception thrown during the exchange, or `"none"` if no exception happened.
|===
.High cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`client.name` _(required)_|Client name derived from the request URI host.
|`http.url` _(required)_|HTTP request URI.
|===
@@ -151,24 +190,27 @@ Instrumentation is using the `org.springframework.http.client.observation.Client
[[integration.observability.http-client.webclient]]
=== WebClient
Applications must configure an `ObservationRegistry` on the `WebClient` builder to enable the instrumentation; without that, observations are "no-ops".
Spring Boot will auto-configure `WebClient.Builder` beans with the observation registry already set.
Instrumentation is using the `org.springframework.web.reactive.function.client.ClientRequestObservationConvention` by default, backed by the `ClientRequestObservationContext`.
.Low cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`exception` _(required)_|Name of the exception thrown during the exchange, or `"none"` if no exception happened.
|`method` _(required)_|Name of HTTP request method or `"none"` if the request could not be created.
|`outcome` _(required)_|Outcome of the HTTP client exchange.
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided. Only the path part of the URI is considered.
|`client.name` _(required)_|Client name derived from the request URI host.
|`status` _(required)_|HTTP response raw status code, or `"IO_ERROR"` in case of `IOException`, or `"CLIENT_ERROR"` if no response was received.
|`uri` _(required)_|URI template used for HTTP request, or `"none"` if none was provided.
|`outcome` _(required)_|Outcome of the HTTP client exchange.
|`exception` _(required)_|Name of the exception thrown during the exchange, or `"none"` if no exception happened.
|===
.High cardinality Keys
[cols="a,a"]
|===
|Name | Description
|`client.name` _(required)_|Client name derived from the request URI host.
|`http.url` _(required)_|HTTP request URI.
|===
@@ -117,6 +117,9 @@ TIP: Note that the `java.net` implementation for HTTP requests can raise an exce
accessing the status of a response that represents an error (such as 401). If this is an
issue, switch to another HTTP client library.
NOTE: `RestTemplate` can be instrumented for observability, in order to produce metrics and traces.
See the <<integration.adoc#integration.observability.http-client.resttemplate,RestTemplate Observability support>> section.
[[rest-resttemplate-uri]]
==== URIs
@@ -136,14 +136,16 @@ To configure the rules that the `TaskExecutor` uses, we expose simple bean prope
[[scheduling-task-scheduler]]
== The Spring `TaskScheduler` Abstraction
In addition to the `TaskExecutor` abstraction, Spring 3.0 introduced a `TaskScheduler`
with a variety of methods for scheduling tasks to run at some point in the future.
The following listing shows the `TaskScheduler` interface definition:
In addition to the `TaskExecutor` abstraction, Spring has a `TaskScheduler` SPI with a
variety of methods for scheduling tasks to run at some point in the future. The following
listing shows the `TaskScheduler` interface definition:
[source,java,indent=0,subs="verbatim,quotes"]
----
public interface TaskScheduler {
Clock getClock();
ScheduledFuture schedule(Runnable task, Trigger trigger);
ScheduledFuture schedule(Runnable task, Instant startTime);
@@ -168,12 +170,11 @@ much more flexible.
[[scheduling-trigger-interface]]
=== `Trigger` Interface
The `Trigger` interface is essentially inspired by JSR-236 which, as of Spring 3.0,
was not yet officially implemented. The basic idea of the `Trigger` is that execution
times may be determined based on past execution outcomes or even arbitrary conditions.
If these determinations take into account the outcome of the preceding execution,
that information is available within a `TriggerContext`. The `Trigger` interface itself
is quite simple, as the following listing shows:
The `Trigger` interface is essentially inspired by JSR-236. The basic idea of the
`Trigger` is that execution times may be determined based on past execution outcomes or
even arbitrary conditions. If these determinations take into account the outcome of the
preceding execution, that information is available within a `TriggerContext`. The
`Trigger` interface itself is quite simple, as the following listing shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -192,6 +193,8 @@ default). The following listing shows the available methods for `Trigger` implem
----
public interface TriggerContext {
Clock getClock();
Instant lastScheduledExecution();
Instant lastActualExecution();
@@ -813,8 +816,7 @@ its properties from the job data mapped to properties of the job instance. So, i
the `ExampleJob` contains a bean property named `timeout`, and the `JobDetail`
has it applied automatically:
[source,java,indent=0]
[subs="verbatim"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package example;
@@ -824,7 +826,7 @@ has it applied automatically:
/**
* Setter called after the ExampleJob is instantiated
* with the value from the JobDetailFactoryBean (5)
* with the value from the JobDetailFactoryBean.
*/
public void setTimeout(int timeout) {
this.timeout = timeout;
@@ -33,7 +33,7 @@ implement. Note that this interface is defined in plain Java. Dependent objects
are injected with a reference to the `Messenger` do not know that the underlying
implementation is a Groovy script. The following listing shows the `Messenger` interface:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.scripting;
@@ -45,7 +45,7 @@ implementation is a Groovy script. The following listing shows the `Messenger` i
The following example defines a class that has a dependency on the `Messenger` interface:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.scripting;
@@ -65,15 +65,14 @@ The following example defines a class that has a dependency on the `Messenger` i
The following example implements the `Messenger` interface in Groovy:
[source,groovy,indent=0,subs="verbatim,quotes"]
[source,groovy,indent=0,subs="verbatim,quotes",chomp="-packages",fold="none"]
----
// from the file 'Messenger.groovy'
package org.springframework.scripting.groovy;
package org.springframework.scripting.groovy
// import the Messenger interface (written in Java) that is to be implemented
// Import the Messenger interface (written in Java) that is to be implemented
import org.springframework.scripting.Messenger
// define the implementation in Groovy
// Define the implementation in Groovy in file 'Messenger.groovy'
class GroovyMessenger implements Messenger {
String message
@@ -276,7 +275,7 @@ surrounded by quotation marks. The following listing shows the changes that you
(the developer) should make to the `Messenger.groovy` source file when the
execution of the program is paused:
[source,groovy,indent=0,subs="verbatim,quotes"]
[source,groovy,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.scripting
@@ -331,13 +330,13 @@ feature works:
<lang:groovy id="messenger">
<lang:inline-script>
package org.springframework.scripting.groovy;
package org.springframework.scripting.groovy
import org.springframework.scripting.Messenger
import org.springframework.scripting.Messenger
class GroovyMessenger implements Messenger {
String message
}
class GroovyMessenger implements Messenger {
String message
}
</lang:inline-script>
<lang:property name="message" value="I Can Do The Frug" />
@@ -363,13 +362,13 @@ constructors and properties 100% clear, the following mixture of code and config
does not work:
.An approach that cannot work
[source,groovy,indent=0,subs="verbatim,quotes"]
[source,groovy,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
// from the file 'Messenger.groovy'
package org.springframework.scripting.groovy;
package org.springframework.scripting.groovy
import org.springframework.scripting.Messenger
// from the file 'Messenger.groovy'
class GroovyMessenger implements Messenger {
GroovyMessenger() {}
@@ -420,7 +419,7 @@ If you have read this chapter straight from the top, you have already
<<dynamic-language-a-first-example, seen an example>> of a Groovy-dynamic-language-backed
bean. Now consider another example (again using an example from the Spring test suite):
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.scripting;
@@ -432,11 +431,11 @@ bean. Now consider another example (again using an example from the Spring test
The following example implements the `Calculator` interface in Groovy:
[source,groovy,indent=0,subs="verbatim,quotes"]
[source,groovy,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
// from the file 'calculator.groovy'
package org.springframework.scripting.groovy
// from the file 'calculator.groovy'
class GroovyCalculator implements Calculator {
int add(int x, int y) {
@@ -457,7 +456,7 @@ The following bean definition uses the calculator defined in Groovy:
Finally, the following small application exercises the preceding configuration:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.scripting;
@@ -596,7 +595,7 @@ Now we can show a fully working example of using a BeanShell-based bean that imp
the `Messenger` interface that was defined earlier in this chapter. We again show the
definition of the `Messenger` interface:
[source,java,indent=0,subs="verbatim,quotes"]
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
package org.springframework.scripting;
@@ -676,9 +675,8 @@ beans, you have to enable the "`refreshable beans`" functionality. See
The following example shows an `org.springframework.web.servlet.mvc.Controller` implemented
by using the Groovy dynamic language:
[source,groovy,indent=0,subs="verbatim,quotes"]
[source,groovy,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
// from the file '/WEB-INF/groovy/FortuneController.groovy'
package org.springframework.showcase.fortune.web
import org.springframework.showcase.fortune.service.FortuneService
@@ -689,6 +687,7 @@ by using the Groovy dynamic language:
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
// from the file '/WEB-INF/groovy/FortuneController.groovy'
class FortuneController implements Controller {
@Property FortuneService fortuneService
@@ -1,5 +1,7 @@
:noheader:
:toc:
:toclevels: 4
:tabsize: 4
include::attributes.adoc[]
= Spring Framework Documentation
Rod Johnson; Juergen Hoeller; Keith Donald; Colin Sampaleanu; Rob Harrop; Thomas Risberg; Alef Arendsen; Darren Davison; Dmitriy Kopylenko; Mark Pollack; Thierry Templier; Erwin Vervaet; Portia Tung; Ben Hale; Adrian Colyer; John Lewis; Costin Leau; Mark Fisher; Sam Brannen; Ramnivas Laddad; Arjen Poutsma; Chris Beams; Tareq Abedrabbo; Andy Clement; Dave Syer; Oliver Gierke; Rossen Stoyanchev; Phillip Webb; Rob Winch; Brian Clozel; Stephane Nicoll; Sebastien Deleuze; Jay Bryant; Mark Paluch
@@ -9,7 +11,7 @@ NOTE: This documentation is also available in {docs-spring-framework}/reference/
[[legal]]
== Legal
Copyright © 2002 - 2022 VMware, Inc. All Rights Reserved.
Copyright © 2002 - 2023 VMware, Inc. All Rights Reserved.
Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
@@ -2022,12 +2022,13 @@ properties.
class ExampleIntegrationTests {
@Container
static RedisContainer redis = new RedisContainer();
static GenericContainer redis =
new GenericContainer("redis:5.0.3-alpine").withExposedPorts(6379);
@DynamicPropertySource
static void redisProperties(DynamicPropertyRegistry registry) {
registry.add("redis.host", redis::getHost);
registry.add("redis.port", redis::getMappedPort);
registry.add("redis.port", redis::getFirstMappedPort);
}
// tests ...
@@ -2045,13 +2046,14 @@ properties.
@Container
@JvmStatic
val redis: RedisContainer = RedisContainer()
val redis: GenericContainer =
GenericContainer("redis:5.0.3-alpine").withExposedPorts(6379)
@DynamicPropertySource
@JvmStatic
fun redisProperties(registry: DynamicPropertyRegistry) {
registry.add("redis.host", redis::getHost)
registry.add("redis.port", redis::getMappedPort)
registry.add("redis.port", redis::getFirstMappedPort)
}
}
@@ -251,7 +251,7 @@ By default global configuration enables the following:
* `GET`, `HEAD`, and `POST` methods.
`allowedCredentials` is not enabled by default, since that establishes a trust level
that exposes sensitive user-specific information( such as cookies and CSRF tokens) and
that exposes sensitive user-specific information (such as cookies and CSRF tokens) and
should be used only where appropriate. When it is enabled either `allowOrigins` must be
set to one or more specific domain (but not the special value `"*"`) or alternatively
the `allowOriginPatterns` property may be used to match to a dynamic set of origins.
@@ -38,6 +38,8 @@ You can also use `WebClient.builder()` with further options:
* `filter`: Client filter for every request.
* `exchangeStrategies`: HTTP message reader/writer customizations.
* `clientConnector`: HTTP client library settings.
* `observationRegistry`: the registry to use for enabling <<integration.adoc#integration.observability.http-client.webclient, Observability support>>.
* `observationConvention`: <<integration.adoc#integration.observability.config,an optional, custom convention to extract metadata>> for recorded observations.
For example:
@@ -613,8 +613,8 @@ The `DefaultServerWebExchange` uses the configured `HttpMessageReader` to parse
----
The `DefaultServerWebExchange` uses the configured
`HttpMessageReader<MultiValueMap<String, Part>>` to parse `multipart/form-data` content
into a `MultiValueMap`.
`HttpMessageReader<MultiValueMap<String, Part>>` to parse `multipart/form-data`,
`multipart/mixed`, and `multipart/related` content into a `MultiValueMap`.
By default, this is the `DefaultPartHttpMessageReader`, which does not have any third-party
dependencies.
Alternatively, the `SynchronossPartHttpMessageReader` can be used, which is based on the
@@ -805,9 +805,9 @@ consistently for access to the cached form data versus reading from the raw requ
==== Multipart
`MultipartHttpMessageReader` and `MultipartHttpMessageWriter` support decoding and
encoding "multipart/form-data" content. In turn `MultipartHttpMessageReader` delegates to
another `HttpMessageReader` for the actual parsing to a `Flux<Part>` and then simply
collects the parts into a `MultiValueMap`.
encoding "multipart/form-data", "multipart/mixed", and "multipart/related" content.
In turn `MultipartHttpMessageReader` delegates to another `HttpMessageReader`
for the actual parsing to a `Flux<Part>` and then simply collects the parts into a `MultiValueMap`.
By default, the `DefaultPartHttpMessageReader` is used, but this can be changed through the
`ServerCodecConfigurer`.
For more information about the `DefaultPartHttpMessageReader`, refer to the
@@ -2504,7 +2504,7 @@ reactive type, as the following example shows:
----
Note that use of `@ModelAttribute` is optional -- for example, to set its attributes.
By default, any argument that is not a simple value type( as determined by
By default, any argument that is not a simple value type (as determined by
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty])
and is not resolved by any other argument resolver is treated as if it were annotated
with `@ModelAttribute`.
@@ -1649,138 +1649,6 @@ is the default type.
[[mvc-view-tiles]]
== Tiles
You can integrate Tiles - just as any other view technology - in web
applications that use Spring. This section describes, in a broad way, how to do so.
NOTE: This section focuses on Spring's support for Tiles version 3 in the
`org.springframework.web.servlet.view.tiles3` package.
[[mvc-view-tiles-dependencies]]
=== Dependencies
To be able to use Tiles, you have to add a dependency on Tiles version 3.0.1 or higher
and https://tiles.apache.org/framework/dependency-management.html[its transitive dependencies]
to your project.
[[mvc-view-tiles-integrate]]
=== Configuration
To be able to use Tiles, you have to configure it by using files that contain definitions
(for basic information on definitions and other Tiles concepts, see
https://tiles.apache.org[]). In Spring, this is done by using the `TilesConfigurer`.
The following example `ApplicationContext` configuration shows how to do so:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/defs/general.xml</value>
<value>/WEB-INF/defs/widgets.xml</value>
<value>/WEB-INF/defs/administrator.xml</value>
<value>/WEB-INF/defs/customer.xml</value>
<value>/WEB-INF/defs/templates.xml</value>
</list>
</property>
</bean>
----
The preceding example defines five files that contain definitions. The files are all
located in the `WEB-INF/defs` directory. At initialization of the `WebApplicationContext`,
the files are loaded, and the definitions factory are initialized. After that has
been done, the Tiles included in the definition files can be used as views within your
Spring web application. To be able to use the views, you have to have a `ViewResolver`
as with any other view technology in Spring: typically a convenient `TilesViewResolver`.
You can specify locale-specific Tiles definitions by adding an underscore and then
the locale, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/defs/tiles.xml</value>
<value>/WEB-INF/defs/tiles_fr_FR.xml</value>
</list>
</property>
</bean>
----
With the preceding configuration, `tiles_fr_FR.xml` is used for requests with the `fr_FR` locale,
and `tiles.xml` is used by default.
NOTE: Since underscores are used to indicate locales, we recommended not using
them otherwise in the file names for Tiles definitions.
[[mvc-view-tiles-url]]
==== `UrlBasedViewResolver`
The `UrlBasedViewResolver` instantiates the given `viewClass` for each view it has to
resolve. The following bean defines a `UrlBasedViewResolver`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView"/>
</bean>
----
[[mvc-view-tiles-preparer]]
==== `SimpleSpringPreparerFactory` and `SpringBeanPreparerFactory`
As an advanced feature, Spring also supports two special Tiles `PreparerFactory`
implementations. See the Tiles documentation for details on how to use
`ViewPreparer` references in your Tiles definition files.
You can specify `SimpleSpringPreparerFactory` to autowire `ViewPreparer` instances based on
specified preparer classes, applying Spring's container callbacks as well as applying
configured Spring BeanPostProcessors. If Spring's context-wide annotation configuration has
been activated, annotations in `ViewPreparer` classes are automatically detected and
applied. Note that this expects preparer classes in the Tiles definition files, as
the default `PreparerFactory` does.
You can specify `SpringBeanPreparerFactory` to operate on specified preparer names (instead
of classes), obtaining the corresponding Spring bean from the DispatcherServlet's
application context. The full bean creation process is in the control of the Spring
application context in this case, allowing for the use of explicit dependency injection
configuration, scoped beans, and so on. Note that you need to define one Spring bean definition
for each preparer name (as used in your Tiles definitions). The following example shows
how to define a `SpringBeanPreparerFactory` property on a `TilesConfigurer` bean:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/defs/general.xml</value>
<value>/WEB-INF/defs/widgets.xml</value>
<value>/WEB-INF/defs/administrator.xml</value>
<value>/WEB-INF/defs/customer.xml</value>
<value>/WEB-INF/defs/templates.xml</value>
</list>
</property>
<!-- resolving preparer names as Spring bean definition names -->
<property name="preparerFactoryClass"
value="org.springframework.web.servlet.view.tiles3.SpringBeanPreparerFactory"/>
</bean>
----
[[mvc-view-feeds]]
== RSS and Atom
@@ -802,8 +802,8 @@ The following table provides more details on the `ViewResolver` hierarchy:
| `InternalResourceViewResolver`
| Convenient subclass of `UrlBasedViewResolver` that supports `InternalResourceView` (in
effect, Servlets and JSPs) and subclasses such as `JstlView` and `TilesView`. You can
specify the view class for all views generated by this resolver by using `setViewClass(..)`.
effect, Servlets and JSPs) and subclasses such as `JstlView`. You can specify the view
class for all views generated by this resolver by using `setViewClass(..)`.
See the {api-spring-framework}/web/reactive/result/view/UrlBasedViewResolver.html[`UrlBasedViewResolver`]
javadoc for details.
@@ -4777,7 +4777,7 @@ directly. For example:
ContextSnapshot snapshot = ContextSnapshot.captureAll();
// On a different thread: restore ThreadLocal values
try (ContextSnapshot.Scope scoped = snapshot.setThreadLocals()) {
try (ContextSnapshot.Scope scope = snapshot.setThreadLocals()) {
// ...
}
----
@@ -5888,7 +5888,7 @@ The following example shows how to achieve the same configuration in XML:
</mvc:view-resolvers>
----
Note, however, that FreeMarker, Tiles, Groovy Markup, and script templates also require
Note, however, that FreeMarker, Groovy Markup, and script templates also require
configuration of the underlying view technology.
The MVC namespace provides dedicated elements. The following example works with FreeMarker:
@@ -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.
@@ -30,6 +30,8 @@ public class AotProcessingSample {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MyApplication.class);
context.refreshForAotProcessing(hints);
// ...
context.close();
// end::aotcontext[]
}
@@ -0,0 +1,42 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.integration.observability.httpserver.reactive;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.filter.reactive.ServerHttpObservationFilter;
import org.springframework.web.server.ServerWebExchange;
@Controller
public class UserController {
@ExceptionHandler(MissingUserException.class)
ResponseEntity<Void> handleMissingUser(ServerWebExchange exchange, MissingUserException exception) {
// We want to record this exception with the observation
ServerHttpObservationFilter.findObservationContext(exchange)
.ifPresent(context -> context.setError(exception));
return ResponseEntity.notFound().build();
}
// @fold:on
@SuppressWarnings("serial")
static class MissingUserException extends RuntimeException {
}
// @fold:off
}
@@ -0,0 +1,43 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.docs.integration.observability.httpserver.servlet;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.filter.ServerHttpObservationFilter;
@Controller
public class UserController {
@ExceptionHandler(MissingUserException.class)
ResponseEntity<Void> handleMissingUser(HttpServletRequest request, MissingUserException exception) {
// We want to record this exception with the observation
ServerHttpObservationFilter.findObservationContext(request)
.ifPresent(context -> context.setError(exception));
return ResponseEntity.notFound().build();
}
// @fold:on
@SuppressWarnings("serial")
static class MissingUserException extends RuntimeException {
}
// @fold:off
}
+25 -25
View File
@@ -7,23 +7,23 @@ javaPlatform {
}
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.14.1"))
api(platform("io.micrometer:micrometer-bom:1.10.3"))
api(platform("io.netty:netty-bom:4.1.86.Final"))
api(platform("com.fasterxml.jackson:jackson-bom:2.14.2"))
api(platform("io.micrometer:micrometer-bom:1.10.4"))
api(platform("io.netty:netty-bom:4.1.90.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2022.0.2"))
api(platform("io.projectreactor:reactor-bom:2022.0.5"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.6"))
api(platform("org.apache.logging.log4j:log4j-bom:2.19.0"))
api(platform("org.eclipse.jetty:jetty-bom:11.0.13"))
api(platform("org.apache.groovy:groovy-bom:4.0.8"))
api(platform("org.apache.logging.log4j:log4j-bom:2.20.0"))
api(platform("org.eclipse.jetty:jetty-bom:11.0.14"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.4.0"))
api(platform("org.junit:junit-bom:5.9.2"))
api(platform("org.mockito:mockito-bom:4.9.0")) // spring-beans tests fail with 4.10+
api(platform("org.mockito:mockito-bom:5.2.0"))
constraints {
api("com.fasterxml:aalto-xml:1.3.1")
api("com.fasterxml.woodstox:woodstox-core:6.4.0")
api("com.fasterxml.woodstox:woodstox-core:6.5.0")
api("com.github.ben-manes.caffeine:caffeine:3.1.2")
api("com.github.librepdf:openpdf:1.3.30")
api("com.google.code.findbugs:findbugs:3.0.1")
@@ -55,9 +55,9 @@ dependencies {
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
api("io.reactivex.rxjava3:rxjava:3.1.5")
api("io.smallrye.reactive:mutiny:1.8.0")
api("io.undertow:undertow-core:2.3.2.Final")
api("io.undertow:undertow-servlet:2.3.2.Final")
api("io.undertow:undertow-websockets-jsr:2.3.2.Final")
api("io.undertow:undertow-core:2.3.4.Final")
api("io.undertow:undertow-servlet:2.3.4.Final")
api("io.undertow:undertow-websockets-jsr:2.3.4.Final")
api("io.vavr:vavr:0.10.4")
api("jakarta.activation:jakarta.activation-api:2.0.1")
api("jakarta.annotation:jakarta.annotation-api:2.0.0")
@@ -87,7 +87,7 @@ dependencies {
api("jaxen:jaxen:1.2.0")
api("junit:junit:4.13.2")
api("net.sf.jopt-simple:jopt-simple:5.0.4")
api("net.sourceforge.htmlunit:htmlunit:2.69.0")
api("net.sourceforge.htmlunit:htmlunit:2.70.0")
api("org.apache-extras.beanshell:bsh:2.0b6")
api("org.apache.activemq:activemq-broker:5.16.2")
api("org.apache.activemq:activemq-kahadb-store:5.16.2")
@@ -97,16 +97,16 @@ dependencies {
api("org.apache.derby:derbyclient:10.16.1.1")
api("org.apache.derby:derbytools:10.16.1.1")
api("org.apache.httpcomponents.client5:httpclient5:5.2.1")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.1")
api("org.apache.poi:poi-ooxml:5.2.3")
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.4")
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.4")
api("org.apache.tomcat:tomcat-util:10.1.4")
api("org.apache.tomcat:tomcat-websocket:10.1.4")
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.7")
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.7")
api("org.apache.tomcat:tomcat-util:10.1.7")
api("org.apache.tomcat:tomcat-websocket:10.1.7")
api("org.aspectj:aspectjrt:1.9.9.1")
api("org.aspectj:aspectjtools:1.9.9.1")
api("org.aspectj:aspectjweaver:1.9.9.1")
api("org.assertj:assertj-core:3.24.1")
api("org.assertj:assertj-core:3.24.2")
api("org.awaitility:awaitility:3.1.6")
api("org.bouncycastle:bcpkix-jdk18on:1.71")
api("org.codehaus.jettison:jettison:1.3.8")
@@ -116,7 +116,7 @@ dependencies {
api("org.eclipse:yasson:2.0.4")
api("org.ehcache:ehcache:3.4.0")
api("org.ehcache:jcache:1.0.1")
api("org.freemarker:freemarker:2.3.31")
api("org.freemarker:freemarker:2.3.32")
// Substitute for "javax.management:jmxremote_optional:1.0.1_04" which
// is not available on Maven Central
api("org.glassfish.external:opendmk_jmxremote_optional_jar:1.0-b01-ea")
@@ -124,7 +124,7 @@ dependencies {
api("org.glassfish:jakarta.el:4.0.2")
api("org.graalvm.sdk:graal-sdk:22.3.0")
api("org.hamcrest:hamcrest:2.2")
api("org.hibernate:hibernate-core-jakarta:5.6.14.Final")
api("org.hibernate:hibernate-core-jakarta:5.6.15.Final")
api("org.hibernate:hibernate-validator:7.0.5.Final")
api("org.hsqldb:hsqldb:2.7.1")
api("org.javamoney:moneta:1.4.2")
@@ -134,15 +134,15 @@ dependencies {
api("org.ogce:xpp3:1.1.6")
api("org.python:jython-standalone:2.7.1")
api("org.quartz-scheduler:quartz:2.3.2")
api("org.seleniumhq.selenium:htmlunit-driver:2.67.0")
api("org.seleniumhq.selenium:htmlunit-driver:2.70.0")
api("org.seleniumhq.selenium:selenium-java:3.141.59")
api("org.skyscreamer:jsonassert:1.5.0")
api("org.slf4j:slf4j-api:2.0.6")
api("org.testng:testng:7.6.1")
api("org.testng:testng:7.7.1")
api("org.webjars:underscorejs:1.8.3")
api("org.webjars:webjars-locator-core:0.52")
api("org.xmlunit:xmlunit-assertj:2.9.0")
api("org.xmlunit:xmlunit-matchers:2.9.0")
api("org.xmlunit:xmlunit-assertj:2.9.1")
api("org.xmlunit:xmlunit-matchers:2.9.1")
api("org.yaml:snakeyaml:1.33")
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.0.4-SNAPSHOT
version=6.0.7
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
+4 -4
View File
@@ -5,10 +5,10 @@
* One can choose the toolchain to use for compiling the MAIN sources and/or compiling
* and running the TEST sources. These options apply to Java, Kotlin and Groovy sources
* when available.
* {@code "./gradlew check -PmainToolchain=17 -PtestToolchain=18"} will use:
* {@code "./gradlew check -PmainToolchain=17 -PtestToolchain=19"} will use:
* <ul>
* <li>a JDK17 toolchain for compiling the main SourceSet
* <li>a JDK18 toolchain for compiling and running the test SourceSet
* <li>a JDK19 toolchain for compiling and running the test SourceSet
* </ul>
*
* By default, the build will fall back to using the current JDK and 17 language level for all sourceSets.
@@ -23,9 +23,9 @@
* {@code
* $ echo JDK17
* /opt/openjdk/java17
* $ echo JDK18
* $ echo JDK19
* /opt/openjdk/java18
* $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK17,JDK18 check
* $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK17,JDK19 check
* }
*
* @author Brian Clozel
@@ -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.
@@ -151,7 +151,7 @@ class ScheduledAndTransactionalAnnotationIntegrationTests {
@Bean
PersistenceExceptionTranslator peTranslator() {
return mock(PersistenceExceptionTranslator.class);
return mock();
}
@Bean
+1 -1
View File
@@ -7,7 +7,7 @@ pluginManagement {
}
plugins {
id "com.gradle.enterprise" version "3.12.1"
id "com.gradle.enterprise" version "3.12.3"
id "io.spring.ge.conventions" version "0.0.13"
}
@@ -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.
@@ -248,38 +248,42 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
}
/**
* Set by creator of this advice object if the argument names are known.
* <p>This could be for example because they have been explicitly specified in XML,
* Set by the creator of this advice object if the argument names are known.
* <p>This could be for example because they have been explicitly specified in XML
* or in an advice annotation.
* @param argNames comma delimited list of arg names
* @param argumentNames comma delimited list of argument names
*/
public void setArgumentNames(String argNames) {
String[] tokens = StringUtils.commaDelimitedListToStringArray(argNames);
public void setArgumentNames(String argumentNames) {
String[] tokens = StringUtils.commaDelimitedListToStringArray(argumentNames);
setArgumentNamesFromStringArray(tokens);
}
public void setArgumentNamesFromStringArray(String... args) {
this.argumentNames = new String[args.length];
for (int i = 0; i < args.length; i++) {
this.argumentNames[i] = args[i].strip();
/**
* Set by the creator of this advice object if the argument names are known.
* <p>This could be for example because they have been explicitly specified in XML
* or in an advice annotation.
* @param argumentNames list of argument names
*/
public void setArgumentNamesFromStringArray(String... argumentNames) {
this.argumentNames = new String[argumentNames.length];
for (int i = 0; i < argumentNames.length; i++) {
this.argumentNames[i] = argumentNames[i].strip();
if (!isVariableName(this.argumentNames[i])) {
throw new IllegalArgumentException(
"'argumentNames' property of AbstractAspectJAdvice contains an argument name '" +
this.argumentNames[i] + "' that is not a valid Java identifier");
}
}
if (this.argumentNames != null) {
if (this.aspectJAdviceMethod.getParameterCount() == this.argumentNames.length + 1) {
// May need to add implicit join point arg name...
Class<?> firstArgType = this.aspectJAdviceMethod.getParameterTypes()[0];
if (firstArgType == JoinPoint.class ||
firstArgType == ProceedingJoinPoint.class ||
firstArgType == JoinPoint.StaticPart.class) {
String[] oldNames = this.argumentNames;
this.argumentNames = new String[oldNames.length + 1];
this.argumentNames[0] = "THIS_JOIN_POINT";
System.arraycopy(oldNames, 0, this.argumentNames, 1, oldNames.length);
}
if (this.aspectJAdviceMethod.getParameterCount() == this.argumentNames.length + 1) {
// May need to add implicit join point arg name...
Class<?> firstArgType = this.aspectJAdviceMethod.getParameterTypes()[0];
if (firstArgType == JoinPoint.class ||
firstArgType == ProceedingJoinPoint.class ||
firstArgType == JoinPoint.StaticPart.class) {
String[] oldNames = this.argumentNames;
this.argumentNames = new String[oldNames.length + 1];
this.argumentNames[0] = "THIS_JOIN_POINT";
System.arraycopy(oldNames, 0, this.argumentNames, 1, oldNames.length);
}
}
}
@@ -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.
@@ -38,6 +38,14 @@ import org.springframework.util.StringUtils;
* for an advice method from the pointcut expression, returning, and throwing clauses.
* If an unambiguous interpretation is not available, it returns {@code null}.
*
* <h3>Algorithm Summary</h3>
* <p>If an unambiguous binding can be deduced, then it is.
* If the advice requirements cannot possibly be satisfied, then {@code null}
* is returned. By setting the {@link #setRaiseExceptions(boolean) raiseExceptions}
* property to {@code true}, descriptive exceptions will be thrown instead of
* returning {@code null} in the case that the parameter names cannot be discovered.
*
* <h3>Algorithm Details</h3>
* <p>This class interprets arguments in the following way:
* <ol>
* <li>If the first parameter of the method is of type {@link JoinPoint}
@@ -65,15 +73,15 @@ import org.springframework.util.StringUtils;
* zero we proceed to the next stage. If {@code a} &gt; 1 then an
* {@code AmbiguousBindingException} is raised. If {@code a} == 1,
* and there are no unbound arguments of type {@code Annotation+},
* then an {@code IllegalArgumentException} is raised. if there is
* then an {@code IllegalArgumentException} is raised. If there is
* exactly one such argument, then the corresponding parameter name is
* assigned the value from the pointcut expression.</li>
* <li>If a returningName has been set, and there are no unbound arguments
* <li>If a {@code returningName} has been set, and there are no unbound arguments
* then an {@code IllegalArgumentException} is raised. If there is
* more than one unbound argument then an
* {@code AmbiguousBindingException} is raised. If there is exactly
* one unbound argument then the corresponding parameter name is assigned
* the value &lt;returningName&gt;.</li>
* the value of the {@code returningName}.</li>
* <li>If there remain unbound arguments, then the pointcut expression is
* examined once more for {@code this}, {@code target}, and
* {@code args} pointcut expressions used in the binding form (binding
@@ -99,20 +107,12 @@ import org.springframework.util.StringUtils;
* <p>The behavior on raising an {@code IllegalArgumentException} or
* {@code AmbiguousBindingException} is configurable to allow this discoverer
* to be used as part of a chain-of-responsibility. By default the condition will
* be logged and the {@code getParameterNames(..)} method will simply return
* be logged and the {@link #getParameterNames(Method)} method will simply return
* {@code null}. If the {@link #setRaiseExceptions(boolean) raiseExceptions}
* property is set to {@code true}, the conditions will be thrown as
* {@code IllegalArgumentException} and {@code AmbiguousBindingException},
* respectively.
*
* <p>Was that perfectly clear? ;)
*
* <p>Short version: If an unambiguous binding can be deduced, then it is.
* If the advice requirements cannot possibly be satisfied, then {@code null}
* is returned. By setting the {@link #setRaiseExceptions(boolean) raiseExceptions}
* property to {@code true}, descriptive exceptions will be thrown instead of
* returning {@code null} in the case that the parameter names cannot be discovered.
*
* @author Adrian Colyer
* @author Juergen Hoeller
* @since 2.0
@@ -197,7 +197,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
/**
* If {@code afterReturning} advice binds the return value, the
* returning variable name must be specified.
* {@code returning} variable name must be specified.
* @param returningName the name of the returning variable
*/
public void setReturningName(@Nullable String returningName) {
@@ -206,18 +206,17 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
/**
* If {@code afterThrowing} advice binds the thrown value, the
* throwing variable name must be specified.
* {@code throwing} variable name must be specified.
* @param throwingName the name of the throwing variable
*/
public void setThrowingName(@Nullable String throwingName) {
this.throwingName = throwingName;
}
/**
* Deduce the parameter names for an advice method.
* <p>See the {@link AspectJAdviceParameterNameDiscoverer class level javadoc}
* for this class for details of the algorithm used.
* <p>See the {@link AspectJAdviceParameterNameDiscoverer class-level javadoc}
* for this class for details on the algorithm used.
* @param method the target {@link Method}
* @return the parameter names
*/
@@ -244,31 +243,18 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
int algorithmicStep = STEP_JOIN_POINT_BINDING;
while ((this.numberOfRemainingUnboundArguments > 0) && algorithmicStep < STEP_FINISHED) {
switch (algorithmicStep++) {
case STEP_JOIN_POINT_BINDING:
case STEP_JOIN_POINT_BINDING -> {
if (!maybeBindThisJoinPoint()) {
maybeBindThisJoinPointStaticPart();
}
break;
case STEP_THROWING_BINDING:
maybeBindThrowingVariable();
break;
case STEP_ANNOTATION_BINDING:
maybeBindAnnotationsFromPointcutExpression();
break;
case STEP_RETURNING_BINDING:
maybeBindReturningVariable();
break;
case STEP_PRIMITIVE_ARGS_BINDING:
maybeBindPrimitiveArgsFromPointcutExpression();
break;
case STEP_THIS_TARGET_ARGS_BINDING:
maybeBindThisOrTargetOrArgsFromPointcutExpression();
break;
case STEP_REFERENCE_PCUT_BINDING:
maybeBindReferencePointcutParameter();
break;
default:
throw new IllegalStateException("Unknown algorithmic step: " + (algorithmicStep - 1));
}
case STEP_THROWING_BINDING -> maybeBindThrowingVariable();
case STEP_ANNOTATION_BINDING -> maybeBindAnnotationsFromPointcutExpression();
case STEP_RETURNING_BINDING -> maybeBindReturningVariable();
case STEP_PRIMITIVE_ARGS_BINDING -> maybeBindPrimitiveArgsFromPointcutExpression();
case STEP_THIS_TARGET_ARGS_BINDING -> maybeBindThisOrTargetOrArgsFromPointcutExpression();
case STEP_REFERENCE_PCUT_BINDING -> maybeBindReferencePointcutParameter();
default -> throw new IllegalStateException("Unknown algorithmic step: " + (algorithmicStep - 1));
}
}
}
@@ -322,7 +308,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
/**
* If the first parameter is of type JoinPoint or ProceedingJoinPoint,bind "thisJoinPoint" as
* If the first parameter is of type JoinPoint or ProceedingJoinPoint, bind "thisJoinPoint" as
* parameter name and return true, else return false.
*/
private boolean maybeBindThisJoinPoint() {
@@ -361,14 +347,14 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
// Second candidate we've found - ambiguous binding
throw new AmbiguousBindingException("Binding of throwing parameter '" +
this.throwingName + "' is ambiguous: could be bound to argument " +
throwableIndex + " or argument " + i);
throwableIndex + " or " + i);
}
}
}
if (throwableIndex == -1) {
throw new IllegalStateException("Binding of throwing parameter '" + this.throwingName
+ "' could not be completed as no available arguments are a subtype of Throwable");
throw new IllegalStateException("Binding of throwing parameter '" + this.throwingName +
"' could not be completed as no available arguments are a subtype of Throwable");
}
else {
bindParameterName(throwableIndex, this.throwingName);
@@ -387,7 +373,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
if (this.returningName != null) {
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Binding of returning parameter '" + this.returningName +
"' is ambiguous, there are " + this.numberOfRemainingUnboundArguments + " candidates.");
"' is ambiguous: there are " + this.numberOfRemainingUnboundArguments + " candidates.");
}
// We're all set... find the unbound parameter, and bind it.
@@ -400,7 +386,6 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
}
/**
* Parse the string pointcut expression looking for:
* &#64;this, &#64;target, &#64;args, &#64;within, &#64;withincode, &#64;annotation.
@@ -444,7 +429,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
int numAnnotationSlots = countNumberOfUnboundAnnotationArguments();
if (numAnnotationSlots > 1) {
throw new AmbiguousBindingException("Found " + varNames.size() +
" potential annotation variable(s), and " +
" potential annotation variable(s) and " +
numAnnotationSlots + " potential argument slots");
}
else if (numAnnotationSlots == 1) {
@@ -465,7 +450,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
}
/*
/**
* If the token starts meets Java identifier conventions, it's in.
*/
@Nullable
@@ -501,7 +486,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
private void maybeBindThisOrTargetOrArgsFromPointcutExpression() {
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments
+ " unbound args at this(),target(),args() binding stage, with no way to determine between them");
+ " unbound args at this()/target()/args() binding stage, with no way to determine between them");
}
List<String> varNames = new ArrayList<>();
@@ -533,10 +518,9 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
}
if (varNames.size() > 1) {
throw new AmbiguousBindingException("Found " + varNames.size() +
" candidate this(), target() or args() variables but only one unbound argument slot");
" candidate this(), target(), or args() variables but only one unbound argument slot");
}
else if (varNames.size() == 1) {
for (int j = 0; j < this.parameterNameBindings.length; j++) {
@@ -609,7 +593,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
// else varNames.size must be 0 and we have nothing to bind.
}
/*
/**
* We've found the start of a binding pointcut at the given index into the
* token array. Now we need to extract the pointcut body and return it.
*/
@@ -662,8 +646,8 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
private void maybeBindPrimitiveArgsFromPointcutExpression() {
int numUnboundPrimitives = countNumberOfUnboundPrimitiveArguments();
if (numUnboundPrimitives > 1) {
throw new AmbiguousBindingException("Found '" + numUnboundPrimitives +
"' unbound primitive arguments with no way to distinguish between them.");
throw new AmbiguousBindingException("Found " + numUnboundPrimitives +
" unbound primitive arguments with no way to distinguish between them.");
}
if (numUnboundPrimitives == 1) {
// Look for arg variable and bind it if we find exactly one...
@@ -709,7 +693,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
return false;
}
/*
/**
* Return {@code true} if the given argument type is a subclass
* of the given supertype.
*/
@@ -737,7 +721,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
return count;
}
/*
/**
* Find the argument index with the given type, and bind the given
* {@code varName} in that position.
*/
@@ -754,22 +738,10 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
/**
* Simple struct to hold the extracted text from a pointcut body, together
* Simple record to hold the extracted text from a pointcut body, together
* with the number of tokens consumed in extracting it.
*/
private static class PointcutBody {
private final int numTokensConsumed;
@Nullable
private final String text;
public PointcutBody(int tokens, @Nullable String text) {
this.numTokensConsumed = tokens;
this.text = text;
}
}
private record PointcutBody(int numTokensConsumed, @Nullable String text) {}
/**
* Thrown in response to an ambiguous binding being detected when
@@ -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.
@@ -117,7 +117,7 @@ public class TypePatternClassFilter implements ClassFilter {
}
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
return (this == obj || (obj instanceof TypePatternClassFilter that &&
ObjectUtils.nullSafeEquals(this.typePattern, that.typePattern)));
}
@@ -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.
@@ -20,7 +20,6 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.StringTokenizer;
@@ -52,6 +51,7 @@ import org.springframework.lang.Nullable;
* @author Rod Johnson
* @author Adrian Colyer
* @author Juergen Hoeller
* @author Sam Brannen
* @since 2.0
*/
public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFactory {
@@ -101,14 +101,6 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
@Override
public void validate(Class<?> aspectClass) throws AopConfigException {
// If the parent has the annotation and isn't abstract it's an error
Class<?> superclass = aspectClass.getSuperclass();
if (superclass.getAnnotation(Aspect.class) != null &&
!Modifier.isAbstract(superclass.getModifiers())) {
throw new AopConfigException("[" + aspectClass.getName() + "] cannot extend concrete aspect [" +
superclass.getName() + "]");
}
AjType<?> ajType = AjTypeSystem.getAjType(aspectClass);
if (!ajType.isAspect()) {
throw new NotAnAtAspectException(aspectClass);
@@ -129,21 +121,21 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
*/
@SuppressWarnings("unchecked")
@Nullable
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) {
AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz);
if (foundAnnotation != null) {
return foundAnnotation;
protected static AspectJAnnotation findAspectJAnnotationOnMethod(Method method) {
for (Class<?> annotationType : ASPECTJ_ANNOTATION_CLASSES) {
AspectJAnnotation annotation = findAnnotation(method, (Class<Annotation>) annotationType);
if (annotation != null) {
return annotation;
}
}
return null;
}
@Nullable
private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {
A result = AnnotationUtils.findAnnotation(method, toLookFor);
if (result != null) {
return new AspectJAnnotation<>(result);
private static AspectJAnnotation findAnnotation(Method method, Class<? extends Annotation> annotationType) {
Annotation annotation = AnnotationUtils.findAnnotation(method, annotationType);
if (annotation != null) {
return new AspectJAnnotation(annotation);
}
else {
return null;
@@ -164,9 +156,8 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
/**
* Class modeling an AspectJ annotation, exposing its type enumeration and
* pointcut String.
* @param <A> the annotation type
*/
protected static class AspectJAnnotation<A extends Annotation> {
protected static class AspectJAnnotation {
private static final String[] EXPRESSION_ATTRIBUTES = {"pointcut", "value"};
@@ -179,7 +170,7 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
AfterThrowing.class, AspectJAnnotationType.AtAfterThrowing //
);
private final A annotation;
private final Annotation annotation;
private final AspectJAnnotationType annotationType;
@@ -187,11 +178,11 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
private final String argumentNames;
public AspectJAnnotation(A annotation) {
public AspectJAnnotation(Annotation annotation) {
this.annotation = annotation;
this.annotationType = determineAnnotationType(annotation);
try {
this.pointcutExpression = resolveExpression(annotation);
this.pointcutExpression = resolvePointcutExpression(annotation);
Object argNames = AnnotationUtils.getValue(annotation, "argNames");
this.argumentNames = (argNames instanceof String names ? names : "");
}
@@ -200,7 +191,7 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
}
}
private AspectJAnnotationType determineAnnotationType(A annotation) {
private AspectJAnnotationType determineAnnotationType(Annotation annotation) {
AspectJAnnotationType type = annotationTypeMap.get(annotation.annotationType());
if (type != null) {
return type;
@@ -208,21 +199,21 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
throw new IllegalStateException("Unknown annotation type: " + annotation);
}
private String resolveExpression(A annotation) {
private String resolvePointcutExpression(Annotation annotation) {
for (String attributeName : EXPRESSION_ATTRIBUTES) {
Object val = AnnotationUtils.getValue(annotation, attributeName);
if (val instanceof String str && !str.isEmpty()) {
return str;
}
}
throw new IllegalStateException("Failed to resolve expression in: " + annotation);
throw new IllegalStateException("Failed to resolve pointcut expression in: " + annotation);
}
public AspectJAnnotationType getAnnotationType() {
return this.annotationType;
}
public A getAnnotation() {
public Annotation getAnnotation() {
return this.annotation;
}
@@ -247,19 +238,22 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
*/
private static class AspectJAnnotationParameterNameDiscoverer implements ParameterNameDiscoverer {
private static final String[] EMPTY_ARRAY = new String[0];
@Override
@Nullable
public String[] getParameterNames(Method method) {
if (method.getParameterCount() == 0) {
return new String[0];
return EMPTY_ARRAY;
}
AspectJAnnotation<?> annotation = findAspectJAnnotationOnMethod(method);
AspectJAnnotation annotation = findAspectJAnnotationOnMethod(method);
if (annotation == null) {
return null;
}
StringTokenizer nameTokens = new StringTokenizer(annotation.getArgumentNames(), ",");
if (nameTokens.countTokens() > 0) {
String[] names = new String[nameTokens.countTokens()];
int numTokens = nameTokens.countTokens();
if (numTokens > 0) {
String[] names = new String[numTokens];
for (int i = 0; i < names.length; i++) {
names[i] = nameTokens.nextToken();
}
@@ -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.
@@ -35,7 +35,8 @@ import org.springframework.lang.Nullable;
/**
* Internal implementation of AspectJPointcutAdvisor.
* Note that there will be one instance of this advisor for each target method.
*
* <p>Note that there will be one instance of this advisor for each target method.
*
* @author Rod Johnson
* @author Juergen Hoeller
@@ -212,7 +213,7 @@ final class InstantiationModelAwarePointcutAdvisorImpl
* creation of the advice.
*/
private void determineAdviceType() {
AspectJAnnotation<?> aspectJAnnotation =
AspectJAnnotation aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(this.aspectJAdviceMethod);
if (aspectJAnnotation == null) {
this.isBeforeAdvice = false;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 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.
@@ -23,10 +23,6 @@ import org.springframework.lang.Nullable;
* Subinterface of {@link org.springframework.aop.aspectj.AspectInstanceFactory}
* that returns {@link AspectMetadata} associated with AspectJ-annotated classes.
*
* <p>Ideally, AspectInstanceFactory would include this method itself, but because
* AspectMetadata uses Java-5-only {@link org.aspectj.lang.reflect.AjType},
* we need to split out this subinterface.
*
* @author Rod Johnson
* @since 2.0
* @see AspectMetadata
@@ -35,13 +31,13 @@ import org.springframework.lang.Nullable;
public interface MetadataAwareAspectInstanceFactory extends AspectInstanceFactory {
/**
* Return the AspectJ AspectMetadata for this factory's aspect.
* Get the AspectJ AspectMetadata for this factory's aspect.
* @return the aspect metadata
*/
AspectMetadata getAspectMetadata();
/**
* Return the best possible creation mutex for this factory.
* Get the best possible creation mutex for this factory.
* @return the mutex object (may be {@code null} for no mutex to use)
* @since 4.3
*/
@@ -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.
@@ -87,7 +87,7 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
new InstanceComparator<>(
Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
(Converter<Method, Annotation>) method -> {
AspectJAnnotation<?> ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
AspectJAnnotation ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
return (ann != null ? ann.getAnnotation() : null);
});
Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
@@ -216,7 +216,7 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
@Nullable
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
AspectJAnnotation<?> aspectJAnnotation =
AspectJAnnotation aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
@@ -240,7 +240,7 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);
AspectJAnnotation<?> aspectJAnnotation =
AspectJAnnotation aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
@@ -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.
@@ -108,14 +108,10 @@ class ConfigBeanDefinitionParser implements BeanDefinitionParser {
List<Element> childElts = DomUtils.getChildElements(element);
for (Element elt: childElts) {
String localName = parserContext.getDelegate().getLocalName(elt);
if (POINTCUT.equals(localName)) {
parsePointcut(elt, parserContext);
}
else if (ADVISOR.equals(localName)) {
parseAdvisor(elt, parserContext);
}
else if (ASPECT.equals(localName)) {
parseAspect(elt, parserContext);
switch (localName) {
case POINTCUT -> parsePointcut(elt, parserContext);
case ADVISOR -> parseAdvisor(elt, parserContext);
case ASPECT -> parseAspect(elt, parserContext);
}
}
@@ -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.
@@ -93,10 +93,16 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu
if (bean instanceof Advised advised) {
if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
// Add our local Advisor to the existing proxy's Advisor chain...
// Add our local Advisor to the existing proxy's Advisor chain.
if (this.beforeExistingAdvisors) {
advised.addAdvisor(0, this.advisor);
}
else if (advised.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE &&
advised.getAdvisorCount() > 0) {
// No target, leave last Advisor in place and add new Advisor right before.
advised.addAdvisor(advised.getAdvisorCount() - 1, this.advisor);
return bean;
}
else {
advised.addAdvisor(this.advisor);
}
@@ -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.
@@ -257,23 +257,21 @@ public abstract class AopProxyUtils {
if (ObjectUtils.isEmpty(arguments)) {
return new Object[0];
}
if (method.isVarArgs()) {
if (method.getParameterCount() == arguments.length) {
Class<?>[] paramTypes = method.getParameterTypes();
int varargIndex = paramTypes.length - 1;
Class<?> varargType = paramTypes[varargIndex];
if (varargType.isArray()) {
Object varargArray = arguments[varargIndex];
if (varargArray instanceof Object[] && !varargType.isInstance(varargArray)) {
Object[] newArguments = new Object[arguments.length];
System.arraycopy(arguments, 0, newArguments, 0, varargIndex);
Class<?> targetElementType = varargType.getComponentType();
int varargLength = Array.getLength(varargArray);
Object newVarargArray = Array.newInstance(targetElementType, varargLength);
System.arraycopy(varargArray, 0, newVarargArray, 0, varargLength);
newArguments[varargIndex] = newVarargArray;
return newArguments;
}
if (method.isVarArgs() && (method.getParameterCount() == arguments.length)) {
Class<?>[] paramTypes = method.getParameterTypes();
int varargIndex = paramTypes.length - 1;
Class<?> varargType = paramTypes[varargIndex];
if (varargType.isArray()) {
Object varargArray = arguments[varargIndex];
if (varargArray instanceof Object[] && !varargType.isInstance(varargArray)) {
Object[] newArguments = new Object[arguments.length];
System.arraycopy(arguments, 0, newArguments, 0, varargIndex);
Class<?> targetElementType = varargType.getComponentType();
int varargLength = Array.getLength(varargArray);
Object newVarargArray = Array.newInstance(targetElementType, varargLength);
System.arraycopy(varargArray, 0, newVarargArray, 0, varargLength);
newArguments[varargIndex] = newVarargArray;
return newArguments;
}
}
}
@@ -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.
@@ -251,11 +251,10 @@ class CglibAopProxy implements AopProxy, Serializable {
private void validateClassIfNecessary(Class<?> proxySuperClass, @Nullable ClassLoader proxyClassLoader) {
if (!this.advised.isOptimize() && logger.isInfoEnabled()) {
synchronized (validatedClasses) {
if (!validatedClasses.containsKey(proxySuperClass)) {
doValidateClass(proxySuperClass, proxyClassLoader,
ClassUtils.getAllInterfacesForClassAsSet(proxySuperClass));
validatedClasses.put(proxySuperClass, Boolean.TRUE);
}
validatedClasses.computeIfAbsent(proxySuperClass, clazz -> {
doValidateClass(clazz, proxyClassLoader, ClassUtils.getAllInterfacesForClassAsSet(clazz));
return Boolean.TRUE;
});
}
}
}
@@ -702,8 +701,7 @@ class CglibAopProxy implements AopProxy, Serializable {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
return processReturnType(proxy, target, method, retVal);
}
finally {
if (target != null && !targetSource.isStatic()) {
@@ -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.
@@ -109,10 +109,8 @@ public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializ
*/
private static boolean hasMatchingIntroductions(Advisor[] advisors, Class<?> actualClass) {
for (Advisor advisor : advisors) {
if (advisor instanceof IntroductionAdvisor ia) {
if (ia.getClassFilter().matches(actualClass)) {
return true;
}
if (advisor instanceof IntroductionAdvisor ia && ia.getClassFilter().matches(actualClass)) {
return true;
}
}
return false;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 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.
@@ -21,20 +21,13 @@ import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.MethodMatcher;
/**
* Internal framework class, combining a MethodInterceptor instance
* with a MethodMatcher for use as an element in the advisor chain.
* Internal framework record, combining a {@link MethodInterceptor} instance
* with a {@link MethodMatcher} for use as an element in the advisor chain.
*
* @author Rod Johnson
* @author Sam Brannen
* @param interceptor the {@code MethodInterceptor}
* @param matcher the {@code MethodMatcher}
*/
class InterceptorAndDynamicMethodMatcher {
final MethodInterceptor interceptor;
final MethodMatcher methodMatcher;
public InterceptorAndDynamicMethodMatcher(MethodInterceptor interceptor, MethodMatcher methodMatcher) {
this.interceptor = interceptor;
this.methodMatcher = methodMatcher;
}
record InterceptorAndDynamicMethodMatcher(MethodInterceptor interceptor, MethodMatcher matcher) {
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -169,8 +169,8 @@ public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Clonea
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
if (dm.matcher().matches(this.method, targetClass, this.arguments)) {
return dm.interceptor().invoke(this);
}
else {
// Dynamic matching failed.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -92,8 +92,7 @@ public class BeanFactoryAdvisorRetrievalHelper {
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException) {
BeanCreationException bce = (BeanCreationException) rootCause;
if (rootCause instanceof BeanCurrentlyInCreationException bce) {
String bceBeanName = bce.getBeanName();
if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
if (logger.isTraceEnabled()) {
@@ -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.
@@ -58,11 +58,11 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private ConfigurableBeanFactory beanFactory;
/** Internally used DefaultListableBeanFactory instances, keyed by bean name. */
private final Map<String, DefaultListableBeanFactory> internalBeanFactories =
new HashMap<>();
private final Map<String, DefaultListableBeanFactory> internalBeanFactories = new HashMap<>();
@Override
@@ -77,6 +77,7 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
/**
* Return the BeanFactory that this TargetSourceCreators runs in.
*/
@Nullable
protected final BeanFactory getBeanFactory() {
return this.beanFactory;
}
@@ -0,0 +1,10 @@
/**
* Various {@link org.springframework.aop.framework.autoproxy.TargetSourceCreator}
* implementations for use with Spring's AOP auto-proxying support.
*/
@NonNullApi
@NonNullFields
package org.springframework.aop.framework.autoproxy.target;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;
@@ -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.
@@ -54,9 +54,9 @@ import org.springframework.util.ClassUtils;
* return type however, such exceptions cannot be transmitted back. In that case an
* {@link AsyncUncaughtExceptionHandler} can be registered to process such exceptions.
*
* <p>As of Spring 3.1.2 the {@code AnnotationAsyncExecutionInterceptor} subclass is
* preferred for use due to its support for executor qualification in conjunction with
* Spring's {@code @Async} annotation.
* <p>Note: the {@code AnnotationAsyncExecutionInterceptor} subclass is preferred
* due to its support for executor qualification in conjunction with Spring's
* {@code @Async} annotation.
*
* @author Juergen Hoeller
* @author Chris Beams
@@ -71,8 +71,8 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport imple
/**
* Create a new instance with a default {@link AsyncUncaughtExceptionHandler}.
* @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
* or {@link java.util.concurrent.ExecutorService}) to delegate to;
* as of 4.2.6, a local executor for this interceptor will be built otherwise
* or {@link java.util.concurrent.ExecutorService}) to delegate to; a local
* executor for this interceptor will be built otherwise
*/
public AsyncExecutionInterceptor(@Nullable Executor defaultExecutor) {
super(defaultExecutor);
@@ -81,8 +81,8 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport imple
/**
* Create a new {@code AsyncExecutionInterceptor}.
* @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
* or {@link java.util.concurrent.ExecutorService}) to delegate to;
* as of 4.2.6, a local executor for this interceptor will be built otherwise
* or {@link java.util.concurrent.ExecutorService}) to delegate to; a local
* executor for this interceptor will be built otherwise
* @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use
*/
public AsyncExecutionInterceptor(@Nullable Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) {
@@ -130,9 +130,12 @@ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport imple
}
/**
* This implementation is a no-op for compatibility in Spring 3.1.2.
* Subclasses may override to provide support for extracting qualifier information,
* e.g. via an annotation on the given method.
* Get the qualifier for a specific executor to use when executing the given
* method.
* <p>The default implementation of this method is effectively a no-op.
* <p>Subclasses may override this method to provide support for extracting
* qualifier information &mdash; for example, via an annotation on the given
* method.
* @return always {@code null}
* @since 3.1.2
* @see #determineAsyncExecutor(Method)
@@ -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.
@@ -55,8 +55,8 @@ class ScopedProxyBeanRegistrationAotProcessor implements BeanRegistrationAotProc
@Override
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
Class<?> beanType = registeredBean.getBeanType().toClass();
if (beanType.equals(ScopedProxyFactoryBean.class)) {
Class<?> beanClass = registeredBean.getBeanClass();
if (beanClass.equals(ScopedProxyFactoryBean.class)) {
String targetBeanName = getTargetBeanName(registeredBean.getMergedBeanDefinition());
BeanDefinition targetBeanDefinition =
getTargetBeanDefinition(registeredBean.getBeanFactory(), targetBeanName);
@@ -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.
@@ -19,6 +19,7 @@ package org.springframework.aop.support;
import java.io.Serializable;
import org.springframework.aop.ClassFilter;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -45,7 +46,7 @@ public class RootClassFilter implements ClassFilter, Serializable {
}
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
return (this == obj || (obj instanceof RootClassFilter that &&
this.clazz.equals(that.clazz)));
}
@@ -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.
@@ -183,7 +183,7 @@ public class AnnotationMatchingPointcut implements Pointcut {
}
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.aop.TargetSource;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
@@ -169,7 +170,7 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
@Override
public boolean equals(Object other) {
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -181,7 +181,7 @@ public class CommonsPool2TargetSource extends AbstractPoolingTargetSource implem
}
/**
* Set whether the call should bock when the pool is exhausted.
* Set whether the call should block when the pool is exhausted.
*/
public void setBlockWhenExhausted(boolean blockWhenExhausted) {
this.blockWhenExhausted = blockWhenExhausted;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -131,7 +131,7 @@ public final class EmptyTargetSource implements TargetSource, Serializable {
}
@Override
public boolean equals(Object other) {
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
@@ -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.
@@ -19,6 +19,7 @@ package org.springframework.aop.target;
import java.io.Serializable;
import org.springframework.aop.TargetSource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -100,7 +101,7 @@ public class HotSwappableTargetSource implements TargetSource, Serializable {
* objects are equal.
*/
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
return (this == obj || (obj instanceof HotSwappableTargetSource that &&
this.target.equals(that.target)));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 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.
@@ -65,7 +65,6 @@ public class LazyInitTargetSource extends AbstractBeanFactoryBasedTargetSource {
@Override
@Nullable
public synchronized Object getTarget() throws BeansException {
if (this.target == null) {
this.target = getBeanFactory().getBean(getTargetBeanName());
@@ -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.
@@ -19,6 +19,7 @@ package org.springframework.aop.target;
import java.io.Serializable;
import org.springframework.aop.TargetSource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -82,7 +83,7 @@ public class SingletonTargetSource implements TargetSource, Serializable {
* targets or the targets are equal.
*/
@Override
public boolean equals(Object other) {
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
@@ -0,0 +1,10 @@
/**
* Support for dynamic, refreshable {@link org.springframework.aop.TargetSource}
* implementations for use with Spring AOP.
*/
@NonNullApi
@NonNullFields
package org.springframework.aop.target.dynamic;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;
@@ -0,0 +1,10 @@
/**
* Various {@link org.springframework.aop.TargetSource} implementations for use
* with Spring AOP.
*/
@NonNullApi
@NonNullFields
package org.springframework.aop.target;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;
@@ -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.
@@ -17,7 +17,6 @@
package org.springframework.aop.aspectj;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
@@ -43,17 +42,17 @@ class AspectJAdviceParameterNameDiscovererTests {
@Test
void noArgs() {
assertParameterNames(getMethod("noArgs"), "execution(* *(..))", new String[0]);
assertParameterNames(getMethod("noArgs"), "execution(* *(..))");
}
@Test
void joinPointOnly() {
assertParameterNames(getMethod("tjp"), "execution(* *(..))", new String[] {"thisJoinPoint"});
assertParameterNames(getMethod("tjp"), "execution(* *(..))", "thisJoinPoint");
}
@Test
void joinPointStaticPartOnly() {
assertParameterNames(getMethod("tjpsp"), "execution(* *(..))", new String[] {"thisJoinPointStaticPart"});
assertParameterNames(getMethod("tjpsp"), "execution(* *(..))", "thisJoinPointStaticPart");
}
@Test
@@ -64,18 +63,18 @@ class AspectJAdviceParameterNameDiscovererTests {
@Test
void oneThrowable() {
assertParameterNames(getMethod("oneThrowable"), "foo()", null, "ex", new String[] {"ex"});
assertParameterNamesExtended(getMethod("oneThrowable"), "foo()", null, "ex", "ex");
}
@Test
void oneJPAndOneThrowable() {
assertParameterNames(getMethod("jpAndOneThrowable"), "foo()", null, "ex", new String[] {"thisJoinPoint", "ex"});
assertParameterNamesExtended(getMethod("jpAndOneThrowable"), "foo()", null, "ex", "thisJoinPoint", "ex");
}
@Test
void oneJPAndTwoThrowables() {
assertException(getMethod("jpAndTwoThrowables"), "foo()", null, "ex", AmbiguousBindingException.class,
"Binding of throwing parameter 'ex' is ambiguous: could be bound to argument 1 or argument 2");
"Binding of throwing parameter 'ex' is ambiguous: could be bound to argument 1 or 2");
}
@Test
@@ -86,13 +85,13 @@ class AspectJAdviceParameterNameDiscovererTests {
@Test
void returning() {
assertParameterNames(getMethod("oneObject"), "foo()", "obj", null, new String[] {"obj"});
assertParameterNamesExtended(getMethod("oneObject"), "foo()", "obj", null, "obj");
}
@Test
void ambiguousReturning() {
assertException(getMethod("twoObjects"), "foo()", "obj", null, AmbiguousBindingException.class,
"Binding of returning parameter 'obj' is ambiguous, there are 2 candidates.");
"Binding of returning parameter 'obj' is ambiguous: there are 2 candidates.");
}
@Test
@@ -103,22 +102,22 @@ class AspectJAdviceParameterNameDiscovererTests {
@Test
void thisBindingOneCandidate() {
assertParameterNames(getMethod("oneObject"), "this(x)", new String[] {"x"});
assertParameterNames(getMethod("oneObject"), "this(x)", "x");
}
@Test
void thisBindingWithAlternateTokenizations() {
assertParameterNames(getMethod("oneObject"), "this( x )", new String[] {"x"});
assertParameterNames(getMethod("oneObject"), "this( x)", new String[] {"x"});
assertParameterNames(getMethod("oneObject"), "this (x )", new String[] {"x"});
assertParameterNames(getMethod("oneObject"), "this(x )", new String[] {"x"});
assertParameterNames(getMethod("oneObject"), "foo() && this(x)", new String[] {"x"});
assertParameterNames(getMethod("oneObject"), "this( x )", "x");
assertParameterNames(getMethod("oneObject"), "this( x)", "x");
assertParameterNames(getMethod("oneObject"), "this (x )", "x");
assertParameterNames(getMethod("oneObject"), "this(x )", "x");
assertParameterNames(getMethod("oneObject"), "foo() && this(x)", "x");
}
@Test
void thisBindingTwoCandidates() {
assertException(getMethod("oneObject"), "this(x) || this(y)", AmbiguousBindingException.class,
"Found 2 candidate this(), target() or args() variables but only one unbound argument slot");
"Found 2 candidate this(), target(), or args() variables but only one unbound argument slot");
}
@Test
@@ -131,22 +130,22 @@ class AspectJAdviceParameterNameDiscovererTests {
@Test
void targetBindingOneCandidate() {
assertParameterNames(getMethod("oneObject"), "target(x)", new String[] {"x"});
assertParameterNames(getMethod("oneObject"), "target(x)", "x");
}
@Test
void targetBindingWithAlternateTokenizations() {
assertParameterNames(getMethod("oneObject"), "target( x )", new String[] {"x"});
assertParameterNames(getMethod("oneObject"), "target( x)", new String[] {"x"});
assertParameterNames(getMethod("oneObject"), "target (x )", new String[] {"x"});
assertParameterNames(getMethod("oneObject"), "target(x )", new String[] {"x"});
assertParameterNames(getMethod("oneObject"), "foo() && target(x)", new String[] {"x"});
assertParameterNames(getMethod("oneObject"), "target( x )", "x");
assertParameterNames(getMethod("oneObject"), "target( x)", "x");
assertParameterNames(getMethod("oneObject"), "target (x )", "x");
assertParameterNames(getMethod("oneObject"), "target(x )", "x");
assertParameterNames(getMethod("oneObject"), "foo() && target(x)", "x");
}
@Test
void targetBindingTwoCandidates() {
assertException(getMethod("oneObject"), "target(x) || target(y)", AmbiguousBindingException.class,
"Found 2 candidate this(), target() or args() variables but only one unbound argument slot");
"Found 2 candidate this(), target(), or args() variables but only one unbound argument slot");
}
@Test
@@ -159,24 +158,24 @@ class AspectJAdviceParameterNameDiscovererTests {
@Test
void argsBindingOneObject() {
assertParameterNames(getMethod("oneObject"), "args(x)", new String[] {"x"});
assertParameterNames(getMethod("oneObject"), "args(x)", "x");
}
@Test
void argsBindingOneObjectTwoCandidates() {
assertException(getMethod("oneObject"), "args(x,y)", AmbiguousBindingException.class,
"Found 2 candidate this(), target() or args() variables but only one unbound argument slot");
"Found 2 candidate this(), target(), or args() variables but only one unbound argument slot");
}
@Test
void ambiguousArgsBinding() {
assertException(getMethod("twoObjects"), "args(x,y)", AmbiguousBindingException.class,
"Still 2 unbound args at this(),target(),args() binding stage, with no way to determine between them");
"Still 2 unbound args at this()/target()/args() binding stage, with no way to determine between them");
}
@Test
void argsOnePrimitive() {
assertParameterNames(getMethod("onePrimitive"), "args(count)", new String[] {"count"});
assertParameterNames(getMethod("onePrimitive"), "args(count)", "count");
}
@Test
@@ -188,37 +187,37 @@ class AspectJAdviceParameterNameDiscovererTests {
@Test
void thisAndPrimitive() {
assertParameterNames(getMethod("oneObjectOnePrimitive"), "args(count) && this(obj)",
new String[] {"obj", "count"});
"obj", "count");
}
@Test
void targetAndPrimitive() {
assertParameterNames(getMethod("oneObjectOnePrimitive"), "args(count) && target(obj)",
new String[] {"obj", "count"});
"obj", "count");
}
@Test
void throwingAndPrimitive() {
assertParameterNames(getMethod("oneThrowableOnePrimitive"), "args(count)", null, "ex",
new String[] {"ex", "count"});
assertParameterNamesExtended(getMethod("oneThrowableOnePrimitive"), "args(count)", null, "ex",
"ex", "count");
}
@Test
void allTogetherNow() {
assertParameterNames(getMethod("theBigOne"), "this(foo) && args(x)", null, "ex",
new String[] {"thisJoinPoint", "ex", "x", "foo"});
assertParameterNamesExtended(getMethod("theBigOne"), "this(foo) && args(x)", null, "ex",
"thisJoinPoint", "ex", "x", "foo");
}
@Test
void referenceBinding() {
assertParameterNames(getMethod("onePrimitive"),"somepc(foo)", new String[] {"foo"});
assertParameterNames(getMethod("onePrimitive"),"somepc(foo)", "foo");
}
@Test
void referenceBindingWithAlternateTokenizations() {
assertParameterNames(getMethod("onePrimitive"),"call(bar *) && somepc(foo)", new String[] {"foo"});
assertParameterNames(getMethod("onePrimitive"),"somepc ( foo )", new String[] {"foo"});
assertParameterNames(getMethod("onePrimitive"),"somepc( foo)", new String[] {"foo"});
assertParameterNames(getMethod("onePrimitive"),"call(bar *) && somepc(foo)", "foo");
assertParameterNames(getMethod("onePrimitive"),"somepc ( foo )", "foo");
assertParameterNames(getMethod("onePrimitive"),"somepc( foo)", "foo");
}
}
@@ -230,38 +229,38 @@ class AspectJAdviceParameterNameDiscovererTests {
@Test
void atThis() {
assertParameterNames(getMethod("oneAnnotation"),"@this(a)", new String[] {"a"});
assertParameterNames(getMethod("oneAnnotation"),"@this(a)", "a");
}
@Test
void atTarget() {
assertParameterNames(getMethod("oneAnnotation"),"@target(a)", new String[] {"a"});
assertParameterNames(getMethod("oneAnnotation"),"@target(a)", "a");
}
@Test
void atArgs() {
assertParameterNames(getMethod("oneAnnotation"),"@args(a)", new String[] {"a"});
assertParameterNames(getMethod("oneAnnotation"),"@args(a)", "a");
}
@Test
void atWithin() {
assertParameterNames(getMethod("oneAnnotation"),"@within(a)", new String[] {"a"});
assertParameterNames(getMethod("oneAnnotation"),"@within(a)", "a");
}
@Test
void atWithincode() {
assertParameterNames(getMethod("oneAnnotation"),"@withincode(a)", new String[] {"a"});
assertParameterNames(getMethod("oneAnnotation"),"@withincode(a)", "a");
}
@Test
void atAnnotation() {
assertParameterNames(getMethod("oneAnnotation"),"@annotation(a)", new String[] {"a"});
assertParameterNames(getMethod("oneAnnotation"),"@annotation(a)", "a");
}
@Test
void ambiguousAnnotationTwoVars() {
assertException(getMethod("twoAnnotations"),"@annotation(a) && @this(x)", AmbiguousBindingException.class,
"Found 2 potential annotation variable(s), and 2 potential argument slots");
"Found 2 potential annotation variable(s) and 2 potential argument slots");
}
@Test
@@ -272,15 +271,14 @@ class AspectJAdviceParameterNameDiscovererTests {
@Test
void annotationMedley() {
assertParameterNames(getMethod("annotationMedley"),"@annotation(a) && args(count) && this(foo)",
null, "ex", new String[] {"ex", "foo", "count", "a"});
assertParameterNamesExtended(getMethod("annotationMedley"),"@annotation(a) && args(count) && this(foo)",
null, "ex", "ex", "foo", "count", "a");
}
@Test
void annotationBinding() {
assertParameterNames(getMethod("pjpAndAnAnnotation"),
"execution(* *(..)) && @annotation(ann)",
new String[] {"thisJoinPoint","ann"});
"execution(* *(..)) && @annotation(ann)", "thisJoinPoint", "ann");
}
}
@@ -296,33 +294,23 @@ class AspectJAdviceParameterNameDiscovererTests {
throw new AssertionError("Bad test specification, no method '" + name + "' found in test class");
}
private void assertParameterNames(Method method, String pointcut, String[] parameterNames) {
assertParameterNames(method, pointcut, null, null, parameterNames);
private void assertParameterNames(Method method, String pointcut, String... parameterNames) {
assertParameterNamesExtended(method, pointcut, null, null, parameterNames);
}
private void assertParameterNames(
Method method, String pointcut, String returning, String throwing, String[] parameterNames) {
private void assertParameterNamesExtended(
Method method, String pointcut, String returning, String throwing, String... parameterNames) {
assertThat(parameterNames.length).as("bad test specification, must have same number of parameter names as method arguments").isEqualTo(method.getParameterCount());
assertThat(parameterNames)
.as("bad test specification, must have same number of parameter names as method arguments")
.hasSize(method.getParameterCount());
AspectJAdviceParameterNameDiscoverer discoverer = new AspectJAdviceParameterNameDiscoverer(pointcut);
discoverer.setRaiseExceptions(true);
discoverer.setReturningName(returning);
discoverer.setThrowingName(throwing);
String[] discoveredNames = discoverer.getParameterNames(method);
String formattedExpectedNames = Arrays.toString(parameterNames);
String formattedActualNames = Arrays.toString(discoveredNames);
assertThat(discoveredNames.length).as("Expecting " + parameterNames.length + " parameter names in return set '" +
formattedExpectedNames + "', but found " + discoveredNames.length +
" '" + formattedActualNames + "'").isEqualTo(parameterNames.length);
for (int i = 0; i < discoveredNames.length; i++) {
assertThat(discoveredNames[i]).as("Parameter names must never be null").isNotNull();
assertThat(discoveredNames[i]).as("Expecting parameter " + i + " to be named '" +
parameterNames[i] + "' but was '" + discoveredNames[i] + "'").isEqualTo(parameterNames[i]);
}
assertThat(discoverer.getParameterNames(method)).isEqualTo(parameterNames);
}
private void assertException(Method method, String pointcut, Class<? extends Throwable> exceptionType, String message) {
@@ -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.
@@ -20,6 +20,7 @@ import java.io.FileNotFoundException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.UndeclaredThrowableException;
import java.rmi.RemoteException;
import java.util.ArrayList;
@@ -76,24 +77,21 @@ abstract class AbstractAspectJAdvisorFactoryTests {
/**
* To be overridden by concrete test subclasses.
* @return the fixture
*/
protected abstract AspectJAdvisorFactory getFixture();
protected abstract AspectJAdvisorFactory getAdvisorFactory();
@Test
void rejectsPerCflowAspect() {
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowAspect(), "someBean")))
assertThatExceptionOfType(AopConfigException.class)
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowAspect(), "someBean")))
.withMessageContaining("PERCFLOW");
}
@Test
void rejectsPerCflowBelowAspect() {
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
assertThatExceptionOfType(AopConfigException.class)
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
.withMessageContaining("PERCFLOWBELOW");
}
@@ -102,9 +100,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
TestBean target = new TestBean();
int realAge = 65;
target.setAge(realAge);
TestBean itb = (TestBean) createProxy(target,
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new PerTargetAspect(), "someBean")),
TestBean.class);
TestBean itb = createProxy(target, TestBean.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerTargetAspect(), "someBean")));
assertThat(itb.getAge()).as("Around advice must NOT apply").isEqualTo(realAge);
Advised advised = (Advised) itb;
@@ -139,15 +136,13 @@ abstract class AbstractAspectJAdvisorFactoryTests {
PerTargetAspect aspect1 = new PerTargetAspect();
aspect1.count = 100;
aspect1.setOrder(10);
advisors.addAll(
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(aspect1, "someBean1")));
advisors.addAll(getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspect1, "someBean1")));
PerTargetAspect aspect2 = new PerTargetAspect();
aspect2.setOrder(5);
advisors.addAll(
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(aspect2, "someBean2")));
advisors.addAll(getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspect2, "someBean2")));
OrderComparator.sort(advisors);
TestBean itb = (TestBean) createProxy(target, advisors, TestBean.class);
TestBean itb = createProxy(target, TestBean.class, advisors);
assertThat(itb.getAge()).as("Around advice must NOT apply").isEqualTo(realAge);
// Hit the method in the per clause to instantiate the aspect
@@ -166,14 +161,12 @@ abstract class AbstractAspectJAdvisorFactoryTests {
List<Advisor> advisors = new ArrayList<>();
PerTargetAspectWithOrderAnnotation10 aspect1 = new PerTargetAspectWithOrderAnnotation10();
aspect1.count = 100;
advisors.addAll(
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(aspect1, "someBean1")));
advisors.addAll(getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspect1, "someBean1")));
PerTargetAspectWithOrderAnnotation5 aspect2 = new PerTargetAspectWithOrderAnnotation5();
advisors.addAll(
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(aspect2, "someBean2")));
advisors.addAll(getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspect2, "someBean2")));
OrderComparator.sort(advisors);
TestBean itb = (TestBean) createProxy(target, advisors, TestBean.class);
TestBean itb = createProxy(target, TestBean.class, advisors);
assertThat(itb.getAge()).as("Around advice must NOT apply").isEqualTo(realAge);
// Hit the method in the per clause to instantiate the aspect
@@ -188,9 +181,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
TestBean target = new TestBean();
int realAge = 65;
target.setAge(realAge);
TestBean itb = (TestBean) createProxy(target,
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new PerThisAspect(), "someBean")),
TestBean.class);
TestBean itb = createProxy(target, TestBean.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerThisAspect(), "someBean")));
assertThat(itb.getAge()).as("Around advice must NOT apply").isEqualTo(realAge);
Advised advised = (Advised) itb;
@@ -225,7 +217,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
int realAge = 65;
target.setAge(realAge);
PerTypeWithinAspectInstanceFactory aif = new PerTypeWithinAspectInstanceFactory();
TestBean itb = (TestBean) createProxy(target, getFixture().getAdvisors(aif), TestBean.class);
TestBean itb = createProxy(target, TestBean.class, getAdvisorFactory().getAdvisors(aif));
assertThat(aif.getInstantiationCount()).as("No method calls").isEqualTo(0);
assertThat(itb.getAge()).as("Around advice must now apply").isEqualTo(0);
@@ -254,7 +246,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
assertThat(itb.getAge()).as("Around advice must still apply").isEqualTo(1);
assertThat(itb.getAge()).as("Around advice must still apply").isEqualTo(2);
TestBean itb2 = (TestBean) createProxy(target, getFixture().getAdvisors(aif), TestBean.class);
TestBean itb2 = createProxy(target, TestBean.class, getAdvisorFactory().getAdvisors(aif));
assertThat(aif.getInstantiationCount()).isEqualTo(1);
assertThat(itb2.getAge()).as("Around advice be independent for second instance").isEqualTo(0);
assertThat(aif.getInstantiationCount()).isEqualTo(2);
@@ -278,10 +270,9 @@ abstract class AbstractAspectJAdvisorFactoryTests {
@Test
void namedPointcutFromAspectLibraryWithBinding() {
TestBean target = new TestBean();
ITestBean itb = (ITestBean) createProxy(target,
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(
new NamedPointcutAspectFromLibraryWithBinding(), "someBean")),
ITestBean.class);
ITestBean itb = createProxy(target, ITestBean.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(
new NamedPointcutAspectFromLibraryWithBinding(), "someBean")));
itb.setAge(10);
assertThat(itb.getAge()).as("Around advice must apply").isEqualTo(20);
assertThat(target.getAge()).isEqualTo(20);
@@ -291,9 +282,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
TestBean target = new TestBean();
int realAge = 65;
target.setAge(realAge);
ITestBean itb = (ITestBean) createProxy(target,
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(aspectInstance, "someBean")),
ITestBean.class);
ITestBean itb = createProxy(target, ITestBean.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspectInstance, "someBean")));
assertThat(itb.getAge()).as("Around advice must apply").isEqualTo(-1);
assertThat(target.getAge()).isEqualTo(realAge);
}
@@ -301,10 +291,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
@Test
void bindingWithSingleArg() {
TestBean target = new TestBean();
ITestBean itb = (ITestBean) createProxy(target,
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new BindingAspectWithSingleArg(), "someBean")),
ITestBean.class);
ITestBean itb = createProxy(target, ITestBean.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new BindingAspectWithSingleArg(), "someBean")));
itb.setAge(10);
assertThat(itb.getAge()).as("Around advice must apply").isEqualTo(20);
assertThat(target.getAge()).isEqualTo(20);
@@ -313,10 +301,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
@Test
void bindingWithMultipleArgsDifferentlyOrdered() {
ManyValuedArgs target = new ManyValuedArgs();
ManyValuedArgs mva = (ManyValuedArgs) createProxy(target,
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new ManyValuedArgs(), "someBean")),
ManyValuedArgs.class);
ManyValuedArgs mva = createProxy(target, ManyValuedArgs.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new ManyValuedArgs(), "someBean")));
String a = "a";
int b = 12;
@@ -334,10 +320,8 @@ abstract class AbstractAspectJAdvisorFactoryTests {
void introductionOnTargetNotImplementingInterface() {
NotLockable notLockableTarget = new NotLockable();
assertThat(notLockableTarget instanceof Lockable).isFalse();
NotLockable notLockable1 = (NotLockable) createProxy(notLockableTarget,
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
NotLockable.class);
NotLockable notLockable1 = createProxy(notLockableTarget, NotLockable.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")));
assertThat(notLockable1 instanceof Lockable).isTrue();
Lockable lockable = (Lockable) notLockable1;
assertThat(lockable.locked()).isFalse();
@@ -345,72 +329,64 @@ abstract class AbstractAspectJAdvisorFactoryTests {
assertThat(lockable.locked()).isTrue();
NotLockable notLockable2Target = new NotLockable();
NotLockable notLockable2 = (NotLockable) createProxy(notLockable2Target,
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
NotLockable.class);
NotLockable notLockable2 = createProxy(notLockable2Target, NotLockable.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")));
assertThat(notLockable2 instanceof Lockable).isTrue();
Lockable lockable2 = (Lockable) notLockable2;
assertThat(lockable2.locked()).isFalse();
notLockable2.setIntValue(1);
lockable2.lock();
assertThatIllegalStateException().isThrownBy(() ->
notLockable2.setIntValue(32));
assertThatIllegalStateException().isThrownBy(() -> notLockable2.setIntValue(32));
assertThat(lockable2.locked()).isTrue();
}
@Test
void introductionAdvisorExcludedFromTargetImplementingInterface() {
assertThat(AopUtils.findAdvisorsThatCanApply(
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeLockable(), "someBean")),
CannotBeUnlocked.class).isEmpty()).isTrue();
assertThat(AopUtils.findAdvisorsThatCanApply(getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(),"someBean")), NotLockable.class)).hasSize(2);
assertThat(AopUtils.findAdvisorsThatCanApply(getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeLockable(),"someBean")), NotLockable.class)).hasSize(2);
}
@Test
void introductionOnTargetImplementingInterface() {
CannotBeUnlocked target = new CannotBeUnlocked();
Lockable proxy = (Lockable) createProxy(target,
Lockable proxy = createProxy(target, CannotBeUnlocked.class,
// Ensure that we exclude
AopUtils.findAdvisorsThatCanApply(
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
CannotBeUnlocked.class
),
CannotBeUnlocked.class);
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")),
CannotBeUnlocked.class));
assertThat(proxy).isInstanceOf(Lockable.class);
Lockable lockable = proxy;
assertThat(lockable.locked()).as("Already locked").isTrue();
lockable.lock();
assertThat(lockable.locked()).as("Real target ignores locking").isTrue();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
lockable.unlock());
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> lockable.unlock());
}
@Test
void introductionOnTargetExcludedByTypePattern() {
ArrayList<Object> target = new ArrayList<>();
List<?> proxy = (List<?>) createProxy(target,
List<?> proxy = createProxy(target, List.class,
AopUtils.findAdvisorsThatCanApply(
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
List.class
),
List.class);
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")),
List.class));
assertThat(proxy instanceof Lockable).as("Type pattern must have excluded mixin").isFalse();
}
@Test
void introductionBasedOnAnnotationMatch_SPR5307() {
void introductionBasedOnAnnotationMatch() { // gh-9980
AnnotatedTarget target = new AnnotatedTargetImpl();
List<Advisor> advisors = getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new MakeAnnotatedTypeModifiable(), "someBean"));
Object proxy = createProxy(target, advisors, AnnotatedTarget.class);
System.out.println(advisors.get(1));
assertThat(proxy instanceof Lockable).isTrue();
Lockable lockable = (Lockable)proxy;
lockable.locked();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeAnnotatedTypeModifiable(), "someBean"));
Object proxy = createProxy(target, AnnotatedTarget.class, advisors);
assertThat(proxy).isInstanceOf(Lockable.class);
Lockable lockable = (Lockable) proxy;
assertThat(lockable.locked()).isFalse();
lockable.lock();
assertThat(lockable.locked()).isTrue();
}
// TODO: Why does this test fail? It hasn't been run before, so it maybe never actually passed...
@@ -419,12 +395,12 @@ abstract class AbstractAspectJAdvisorFactoryTests {
void introductionWithArgumentBinding() {
TestBean target = new TestBean();
List<Advisor> advisors = getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new MakeITestBeanModifiable(), "someBean"));
advisors.addAll(getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")));
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeITestBeanModifiable(), "someBean"));
advisors.addAll(getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeLockable(), "someBean")));
Modifiable modifiable = (Modifiable) createProxy(target, advisors, ITestBean.class);
Modifiable modifiable = (Modifiable) createProxy(target, ITestBean.class, advisors);
assertThat(modifiable).isInstanceOf(Modifiable.class);
Lockable lockable = (Lockable) modifiable;
assertThat(lockable.locked()).isFalse();
@@ -443,8 +419,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
lockable.lock();
assertThat(lockable.locked()).isTrue();
assertThatIllegalStateException().as("Should be locked").isThrownBy(() ->
itb.setName("Else"));
assertThatIllegalStateException().as("Should be locked").isThrownBy(() -> itb.setName("Else"));
lockable.unlock();
itb.setName("Tony");
}
@@ -453,12 +428,11 @@ abstract class AbstractAspectJAdvisorFactoryTests {
void aspectMethodThrowsExceptionLegalOnSignature() {
TestBean target = new TestBean();
UnsupportedOperationException expectedException = new UnsupportedOperationException();
List<Advisor> advisors = getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
assertThat(advisors.size()).as("One advice method was found").isEqualTo(1);
ITestBean itb = (ITestBean) createProxy(target, advisors, ITestBean.class);
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(
itb::getAge);
ITestBean itb = createProxy(target, ITestBean.class, advisors);
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(itb::getAge);
}
// TODO document this behaviour.
@@ -467,39 +441,23 @@ abstract class AbstractAspectJAdvisorFactoryTests {
void aspectMethodThrowsExceptionIllegalOnSignature() {
TestBean target = new TestBean();
RemoteException expectedException = new RemoteException();
List<Advisor> advisors = getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
assertThat(advisors.size()).as("One advice method was found").isEqualTo(1);
ITestBean itb = (ITestBean) createProxy(target, advisors, ITestBean.class);
assertThatExceptionOfType(UndeclaredThrowableException.class).isThrownBy(
itb::getAge).withCause(expectedException);
}
protected Object createProxy(Object target, List<Advisor> advisors, Class<?>... interfaces) {
ProxyFactory pf = new ProxyFactory(target);
if (interfaces.length > 1 || interfaces[0].isInterface()) {
pf.setInterfaces(interfaces);
}
else {
pf.setProxyTargetClass(true);
}
// Required everywhere we use AspectJ proxies
pf.addAdvice(ExposeInvocationInterceptor.INSTANCE);
pf.addAdvisors(advisors);
pf.setExposeProxy(true);
return pf.getProxy();
ITestBean itb = createProxy(target, ITestBean.class, advisors);
assertThatExceptionOfType(UndeclaredThrowableException.class)
.isThrownBy(itb::getAge)
.withCause(expectedException);
}
@Test
void twoAdvicesOnOneAspect() {
TestBean target = new TestBean();
TwoAdviceAspect twoAdviceAspect = new TwoAdviceAspect();
List<Advisor> advisors = getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(twoAdviceAspect, "someBean"));
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(twoAdviceAspect, "someBean"));
assertThat(advisors.size()).as("Two advice methods found").isEqualTo(2);
ITestBean itb = (ITestBean) createProxy(target, advisors, ITestBean.class);
ITestBean itb = createProxy(target, ITestBean.class, advisors);
itb.setName("");
assertThat(itb.getAge()).isEqualTo(0);
int newAge = 32;
@@ -510,40 +468,76 @@ abstract class AbstractAspectJAdvisorFactoryTests {
@Test
void afterAdviceTypes() throws Exception {
InvocationTrackingAspect aspect = new InvocationTrackingAspect();
List<Advisor> advisors = getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(aspect, "exceptionHandlingAspect"));
Echo echo = (Echo) createProxy(new Echo(), advisors, Echo.class);
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(aspect, "exceptionHandlingAspect"));
Echo echo = createProxy(new Echo(), Echo.class, advisors);
assertThat(aspect.invocations).isEmpty();
assertThat(echo.echo(42)).isEqualTo(42);
assertThat(aspect.invocations).containsExactly("around - start", "before", "after returning", "after", "around - end");
aspect.invocations.clear();
assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() -> echo.echo(new FileNotFoundException()));
assertThatExceptionOfType(FileNotFoundException.class)
.isThrownBy(() -> echo.echo(new FileNotFoundException()));
assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end");
}
@Test
void nonAbstractParentAspect() throws Exception {
IncrementingAspect aspect = new IncrementingAspect();
// Precondition:
assertThat(Modifier.isAbstract(aspect.getClass().getSuperclass().getModifiers())).isFalse();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(aspectInstanceFactory(aspect, "incrementingAspect"));
ITestBean proxy = createProxy(new TestBean("Jane", 42), ITestBean.class, advisors);
assertThat(proxy.getAge()).isEqualTo(86); // (42 + 1) * 2
}
@Test
void failureWithoutExplicitDeclarePrecedence() {
TestBean target = new TestBean();
MetadataAwareAspectInstanceFactory aspectInstanceFactory = new SingletonMetadataAwareAspectInstanceFactory(
MetadataAwareAspectInstanceFactory aspectInstanceFactory = aspectInstanceFactory(
new NoDeclarePrecedenceShouldFail(), "someBean");
ITestBean itb = (ITestBean) createProxy(target,
getFixture().getAdvisors(aspectInstanceFactory), ITestBean.class);
itb.getAge();
ITestBean itb = createProxy(target, ITestBean.class, getAdvisorFactory().getAdvisors(aspectInstanceFactory));
assertThat(itb.getAge()).isEqualTo(42);
}
@Test
void declarePrecedenceNotSupported() {
TestBean target = new TestBean();
assertThatIllegalArgumentException().isThrownBy(() -> {
MetadataAwareAspectInstanceFactory aspectInstanceFactory = new SingletonMetadataAwareAspectInstanceFactory(
MetadataAwareAspectInstanceFactory aspectInstanceFactory = aspectInstanceFactory(
new DeclarePrecedenceShouldSucceed(), "someBean");
createProxy(target, getFixture().getAdvisors(aspectInstanceFactory), ITestBean.class);
createProxy(target, ITestBean.class, getAdvisorFactory().getAdvisors(aspectInstanceFactory));
});
}
private static MetadataAwareAspectInstanceFactory aspectInstanceFactory(Object aspectInstance, String aspectName) {
return new SingletonMetadataAwareAspectInstanceFactory(aspectInstance, aspectName);
}
@SuppressWarnings("unchecked")
private static <T> T createProxy(Object target, Class<T> type, List<Advisor> advisors) {
ProxyFactory pf = new ProxyFactory(target);
if (type.isInterface()) {
pf.addInterface(type);
}
else {
pf.setProxyTargetClass(true);
}
// Required everywhere we use AspectJ proxies
pf.addAdvice(ExposeInvocationInterceptor.INSTANCE);
pf.addAdvisors(advisors);
pf.setExposeProxy(true);
return (T) pf.getProxy();
}
@Aspect("percflow(execution(* *(..)))")
static class PerCflowAspect {
}
@@ -758,15 +752,35 @@ abstract class AbstractAspectJAdvisorFactoryTests {
static class Echo {
Object echo(Object o) throws Exception {
if (o instanceof Exception) {
throw (Exception) o;
Object echo(Object obj) throws Exception {
if (obj instanceof Exception ex) {
throw ex;
}
return o;
return obj;
}
}
@Aspect
class DoublingAspect {
@Around("execution(* getAge())")
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
return ((int) pjp.proceed()) * 2;
}
}
@Aspect
class IncrementingAspect extends DoublingAspect {
@Around("execution(* getAge())")
public int incrementAge(ProceedingJoinPoint pjp) throws Throwable {
return ((int) pjp.proceed()) + 1;
}
}
@Aspect
private static class InvocationTrackingAspect {
@@ -824,7 +838,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
@Around("getAge()")
int preventExecution(ProceedingJoinPoint pjp) {
return 666;
return 42;
}
}
@@ -844,7 +858,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
@Around("getAge()")
int preventExecution(ProceedingJoinPoint pjp) {
return 666;
return 42;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -38,56 +38,55 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* @author Adrian Colyer
* @author Juergen Hoeller
* @author Chris Beams
* @author Sam Brannen
*/
public class ArgumentBindingTests {
class ArgumentBindingTests {
@Test
public void testBindingInPointcutUsedByAdvice() {
TestBean tb = new TestBean();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(tb);
void bindingInPointcutUsedByAdvice() {
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
proxyFactory.addAspect(NamedPointcutWithArgs.class);
ITestBean proxiedTestBean = proxyFactory.getProxy();
assertThatIllegalArgumentException().isThrownBy(() ->
proxiedTestBean.setName("Supercalifragalisticexpialidocious"));
assertThatIllegalArgumentException()
.isThrownBy(() -> proxiedTestBean.setName("enigma"))
.withMessage("enigma");
}
@Test
public void testAnnotationArgumentNameBinding() {
TransactionalBean tb = new TransactionalBean();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(tb);
void annotationArgumentNameBinding() {
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TransactionalBean());
proxyFactory.addAspect(PointcutWithAnnotationArgument.class);
ITransactionalBean proxiedTestBean = proxyFactory.getProxy();
assertThatIllegalStateException().isThrownBy(
proxiedTestBean::doInTransaction);
assertThatIllegalStateException()
.isThrownBy(proxiedTestBean::doInTransaction)
.withMessage("Invoked with @Transactional");
}
@Test
public void testParameterNameDiscoverWithReferencePointcut() throws Exception {
void parameterNameDiscoverWithReferencePointcut() throws Exception {
AspectJAdviceParameterNameDiscoverer discoverer =
new AspectJAdviceParameterNameDiscoverer("somepc(formal) && set(* *)");
discoverer.setRaiseExceptions(true);
Method methodUsedForParameterTypeDiscovery =
getClass().getMethod("methodWithOneParam", String.class);
String[] pnames = discoverer.getParameterNames(methodUsedForParameterTypeDiscovery);
assertThat(pnames.length).as("one parameter name").isEqualTo(1);
assertThat(pnames[0]).isEqualTo("formal");
Method method = getClass().getDeclaredMethod("methodWithOneParam", String.class);
assertThat(discoverer.getParameterNames(method)).containsExactly("formal");
}
public void methodWithOneParam(String aParam) {
@SuppressWarnings("unused")
private void methodWithOneParam(String aParam) {
}
public interface ITransactionalBean {
interface ITransactionalBean {
@Transactional
void doInTransaction();
}
public static class TransactionalBean implements ITransactionalBean {
static class TransactionalBean implements ITransactionalBean {
@Override
@Transactional
@@ -95,38 +94,35 @@ public class ArgumentBindingTests {
}
}
}
/**
* Mimics Spring's @Transactional annotation without actually introducing the dependency.
*/
@Retention(RetentionPolicy.RUNTIME)
@interface Transactional {
}
/**
* Represents Spring's Transactional annotation without actually introducing the dependency
*/
@Retention(RetentionPolicy.RUNTIME)
@interface Transactional {
}
@Aspect
static class PointcutWithAnnotationArgument {
@Around(value = "execution(* org.springframework..*.*(..)) && @annotation(transactional)")
public Object around(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
throw new IllegalStateException("Invoked with @Transactional");
}
@Aspect
class PointcutWithAnnotationArgument {
}
@Aspect
static class NamedPointcutWithArgs {
@Pointcut("execution(* *(..)) && args(s,..)")
public void pointcutWithArgs(String s) {}
@Around("pointcutWithArgs(aString)")
public Object doAround(ProceedingJoinPoint pjp, String aString) throws Throwable {
throw new IllegalArgumentException(aString);
}
@Around(value = "execution(* org.springframework..*.*(..)) && @annotation(transaction)")
public Object around(ProceedingJoinPoint pjp, Transactional transaction) throws Throwable {
System.out.println("Invoked with transaction " + transaction);
throw new IllegalStateException();
}
}
@Aspect
class NamedPointcutWithArgs {
@Pointcut("execution(* *(..)) && args(s,..)")
public void pointcutWithArgs(String s) {}
@Around("pointcutWithArgs(aString)")
public Object doAround(ProceedingJoinPoint pjp, String aString) throws Throwable {
System.out.println("got '" + aString + "' at '" + pjp + "'");
throw new IllegalArgumentException(aString);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ package org.springframework.aop.aspectj.annotation;
class ReflectiveAspectJAdvisorFactoryTests extends AbstractAspectJAdvisorFactoryTests {
@Override
protected AspectJAdvisorFactory getFixture() {
protected AspectJAdvisorFactory getAdvisorFactory() {
return new ReflectiveAspectJAdvisorFactory();
}
@@ -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.
@@ -50,7 +50,7 @@ public class ThrowsAdviceInterceptorTests {
MyThrowsHandler th = new MyThrowsHandler();
ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th);
Object ret = new Object();
MethodInvocation mi = mock(MethodInvocation.class);
MethodInvocation mi = mock();
given(mi.proceed()).willReturn(ret);
assertThat(ti.invoke(mi)).isEqualTo(ret);
assertThat(th.getCalls()).isEqualTo(0);
@@ -62,7 +62,7 @@ public class ThrowsAdviceInterceptorTests {
ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th);
assertThat(ti.getHandlerMethodCount()).isEqualTo(2);
Exception ex = new Exception();
MethodInvocation mi = mock(MethodInvocation.class);
MethodInvocation mi = mock();
given(mi.proceed()).willThrow(ex);
assertThatException().isThrownBy(() -> ti.invoke(mi)).isSameAs(ex);
assertThat(th.getCalls()).isEqualTo(0);
@@ -73,7 +73,7 @@ public class ThrowsAdviceInterceptorTests {
MyThrowsHandler th = new MyThrowsHandler();
ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th);
FileNotFoundException ex = new FileNotFoundException();
MethodInvocation mi = mock(MethodInvocation.class);
MethodInvocation mi = mock();
given(mi.getMethod()).willReturn(Object.class.getMethod("hashCode"));
given(mi.getThis()).willReturn(new Object());
given(mi.proceed()).willThrow(ex);
@@ -90,7 +90,7 @@ public class ThrowsAdviceInterceptorTests {
ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th);
// Extends RemoteException
ConnectException ex = new ConnectException("");
MethodInvocation mi = mock(MethodInvocation.class);
MethodInvocation mi = mock();
given(mi.proceed()).willThrow(ex);
assertThatExceptionOfType(ConnectException.class).isThrownBy(() ->
ti.invoke(mi))
@@ -115,7 +115,7 @@ public class ThrowsAdviceInterceptorTests {
ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th);
// Extends RemoteException
ConnectException ex = new ConnectException("");
MethodInvocation mi = mock(MethodInvocation.class);
MethodInvocation mi = mock();
given(mi.proceed()).willThrow(ex);
assertThatExceptionOfType(Throwable.class).isThrownBy(() ->
ti.invoke(mi))
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -94,12 +94,11 @@ public class CustomizableTraceInterceptorTests {
@Test
public void testSunnyDayPathLogsCorrectly() throws Throwable {
MethodInvocation methodInvocation = mock(MethodInvocation.class);
MethodInvocation methodInvocation = mock();
given(methodInvocation.getMethod()).willReturn(String.class.getMethod("toString"));
given(methodInvocation.getThis()).willReturn(this);
Log log = mock(Log.class);
Log log = mock();
given(log.isTraceEnabled()).willReturn(true);
CustomizableTraceInterceptor interceptor = new StubCustomizableTraceInterceptor(log);
@@ -110,15 +109,14 @@ public class CustomizableTraceInterceptorTests {
@Test
public void testExceptionPathLogsCorrectly() throws Throwable {
MethodInvocation methodInvocation = mock(MethodInvocation.class);
MethodInvocation methodInvocation = mock();
IllegalArgumentException exception = new IllegalArgumentException();
given(methodInvocation.getMethod()).willReturn(String.class.getMethod("toString"));
given(methodInvocation.getThis()).willReturn(this);
given(methodInvocation.proceed()).willThrow(exception);
Log log = mock(Log.class);
Log log = mock();
given(log.isTraceEnabled()).willReturn(true);
CustomizableTraceInterceptor interceptor = new StubCustomizableTraceInterceptor(log);
@@ -131,15 +129,14 @@ public class CustomizableTraceInterceptorTests {
@Test
public void testSunnyDayPathLogsCorrectlyWithPrettyMuchAllPlaceholdersMatching() throws Throwable {
MethodInvocation methodInvocation = mock(MethodInvocation.class);
MethodInvocation methodInvocation = mock();
given(methodInvocation.getMethod()).willReturn(String.class.getMethod("toString", new Class[0]));
given(methodInvocation.getThis()).willReturn(this);
given(methodInvocation.getArguments()).willReturn(new Object[]{"$ One \\$", 2L});
given(methodInvocation.proceed()).willReturn("Hello!");
Log log = mock(Log.class);
Log log = mock();
given(log.isTraceEnabled()).willReturn(true);
CustomizableTraceInterceptor interceptor = new StubCustomizableTraceInterceptor(log);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -39,10 +39,9 @@ public class DebugInterceptorTests {
@Test
public void testSunnyDayPathLogsCorrectly() throws Throwable {
MethodInvocation methodInvocation = mock();
MethodInvocation methodInvocation = mock(MethodInvocation.class);
Log log = mock(Log.class);
Log log = mock();
given(log.isTraceEnabled()).willReturn(true);
DebugInterceptor interceptor = new StubDebugInterceptor(log);
@@ -54,13 +53,12 @@ public class DebugInterceptorTests {
@Test
public void testExceptionPathStillLogsCorrectly() throws Throwable {
MethodInvocation methodInvocation = mock(MethodInvocation.class);
MethodInvocation methodInvocation = mock();
IllegalArgumentException exception = new IllegalArgumentException();
given(methodInvocation.proceed()).willThrow(exception);
Log log = mock(Log.class);
Log log = mock();
given(log.isTraceEnabled()).willReturn(true);
DebugInterceptor interceptor = new StubDebugInterceptor(log);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -50,10 +50,10 @@ public class PerformanceMonitorInterceptorTests {
@Test
public void testSunnyDayPathLogsPerformanceMetricsCorrectly() throws Throwable {
MethodInvocation mi = mock(MethodInvocation.class);
MethodInvocation mi = mock();
given(mi.getMethod()).willReturn(String.class.getMethod("toString", new Class[0]));
Log log = mock(Log.class);
Log log = mock();
PerformanceMonitorInterceptor interceptor = new PerformanceMonitorInterceptor(true);
interceptor.invokeUnderTrace(mi, log);
@@ -63,11 +63,11 @@ public class PerformanceMonitorInterceptorTests {
@Test
public void testExceptionPathStillLogsPerformanceMetricsCorrectly() throws Throwable {
MethodInvocation mi = mock(MethodInvocation.class);
MethodInvocation mi = mock();
given(mi.getMethod()).willReturn(String.class.getMethod("toString", new Class[0]));
given(mi.proceed()).willThrow(new IllegalArgumentException());
Log log = mock(Log.class);
Log log = mock();
PerformanceMonitorInterceptor interceptor = new PerformanceMonitorInterceptor(true);
assertThatIllegalArgumentException().isThrownBy(() ->
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -38,11 +38,11 @@ public class SimpleTraceInterceptorTests {
@Test
public void testSunnyDayPathLogsCorrectly() throws Throwable {
MethodInvocation mi = mock(MethodInvocation.class);
MethodInvocation mi = mock();
given(mi.getMethod()).willReturn(String.class.getMethod("toString"));
given(mi.getThis()).willReturn(this);
Log log = mock(Log.class);
Log log = mock();
SimpleTraceInterceptor interceptor = new SimpleTraceInterceptor(true);
interceptor.invokeUnderTrace(mi, log);
@@ -52,13 +52,13 @@ public class SimpleTraceInterceptorTests {
@Test
public void testExceptionPathStillLogsCorrectly() throws Throwable {
MethodInvocation mi = mock(MethodInvocation.class);
MethodInvocation mi = mock();
given(mi.getMethod()).willReturn(String.class.getMethod("toString"));
given(mi.getThis()).willReturn(this);
IllegalArgumentException exception = new IllegalArgumentException();
given(mi.proceed()).willThrow(exception);
Log log = mock(Log.class);
Log log = mock();
final SimpleTraceInterceptor interceptor = new SimpleTraceInterceptor(true);
assertThatIllegalArgumentException().isThrownBy(() ->
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,7 +59,7 @@ public class DefaultScopedObjectTests {
}
private static void testBadTargetBeanName(final String badTargetBeanName) {
ConfigurableBeanFactory factory = mock(ConfigurableBeanFactory.class);
ConfigurableBeanFactory factory = mock();
new DefaultScopedObject(factory, badTargetBeanName);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -44,22 +44,22 @@ import static org.mockito.Mockito.mock;
* @author Chris Beams
* @since 13.05.2003
*/
public class DelegatingIntroductionInterceptorTests {
class DelegatingIntroductionInterceptorTests {
@Test
public void testNullTarget() throws Exception {
void testNullTarget() throws Exception {
// Shouldn't accept null target
assertThatIllegalArgumentException().isThrownBy(() ->
new DelegatingIntroductionInterceptor(null));
}
@Test
public void testIntroductionInterceptorWithDelegation() throws Exception {
void testIntroductionInterceptorWithDelegation() throws Exception {
TestBean raw = new TestBean();
assertThat(! (raw instanceof TimeStamped)).isTrue();
ProxyFactory factory = new ProxyFactory(raw);
TimeStamped ts = mock(TimeStamped.class);
TimeStamped ts = mock();
long timestamp = 111L;
given(ts.getTimeStamp()).willReturn(timestamp);
@@ -70,12 +70,12 @@ public class DelegatingIntroductionInterceptorTests {
}
@Test
public void testIntroductionInterceptorWithInterfaceHierarchy() throws Exception {
void testIntroductionInterceptorWithInterfaceHierarchy() throws Exception {
TestBean raw = new TestBean();
assertThat(! (raw instanceof SubTimeStamped)).isTrue();
ProxyFactory factory = new ProxyFactory(raw);
TimeStamped ts = mock(SubTimeStamped.class);
SubTimeStamped ts = mock();
long timestamp = 111L;
given(ts.getTimeStamp()).willReturn(timestamp);
@@ -86,12 +86,12 @@ public class DelegatingIntroductionInterceptorTests {
}
@Test
public void testIntroductionInterceptorWithSuperInterface() throws Exception {
void testIntroductionInterceptorWithSuperInterface() throws Exception {
TestBean raw = new TestBean();
assertThat(! (raw instanceof TimeStamped)).isTrue();
ProxyFactory factory = new ProxyFactory(raw);
TimeStamped ts = mock(SubTimeStamped.class);
SubTimeStamped ts = mock();
long timestamp = 111L;
given(ts.getTimeStamp()).willReturn(timestamp);
@@ -103,7 +103,7 @@ public class DelegatingIntroductionInterceptorTests {
}
@Test
public void testAutomaticInterfaceRecognitionInDelegate() throws Exception {
void testAutomaticInterfaceRecognitionInDelegate() throws Exception {
final long t = 1001L;
class Tester implements TimeStamped, ITester {
@Override
@@ -133,7 +133,7 @@ public class DelegatingIntroductionInterceptorTests {
@Test
public void testAutomaticInterfaceRecognitionInSubclass() throws Exception {
void testAutomaticInterfaceRecognitionInSubclass() throws Exception {
final long t = 1001L;
@SuppressWarnings("serial")
class TestII extends DelegatingIntroductionInterceptor implements TimeStamped, ITester {
@@ -179,7 +179,7 @@ public class DelegatingIntroductionInterceptorTests {
@SuppressWarnings("serial")
@Test
public void testIntroductionInterceptorDoesntReplaceToString() throws Exception {
void testIntroductionInterceptorDoesntReplaceToString() throws Exception {
TestBean raw = new TestBean();
assertThat(! (raw instanceof TimeStamped)).isTrue();
ProxyFactory factory = new ProxyFactory(raw);
@@ -200,7 +200,7 @@ public class DelegatingIntroductionInterceptorTests {
}
@Test
public void testDelegateReturnsThisIsMassagedToReturnProxy() {
void testDelegateReturnsThisIsMassagedToReturnProxy() {
NestedTestBean target = new NestedTestBean();
String company = "Interface21";
target.setCompany(company);
@@ -221,7 +221,7 @@ public class DelegatingIntroductionInterceptorTests {
}
@Test
public void testSerializableDelegatingIntroductionInterceptorSerializable() throws Exception {
void testSerializableDelegatingIntroductionInterceptorSerializable() throws Exception {
SerializablePerson serializableTarget = new SerializablePerson();
String name = "Tony";
serializableTarget.setName("Tony");
@@ -246,7 +246,7 @@ public class DelegatingIntroductionInterceptorTests {
// Test when target implements the interface: should get interceptor by preference.
@Test
public void testIntroductionMasksTargetImplementation() throws Exception {
void testIntroductionMasksTargetImplementation() throws Exception {
final long t = 1001L;
@SuppressWarnings("serial")
class TestII extends DelegatingIntroductionInterceptor implements TimeStamped {
@@ -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.
@@ -21,6 +21,8 @@ import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.springframework.lang.Nullable;
/**
* Abstract superclass for counting advices etc.
*
@@ -59,7 +61,7 @@ public class MethodCounter implements Serializable {
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object other) {
public boolean equals(@Nullable Object other) {
return (other != null && other.getClass() == this.getClass());
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.springframework.aop.testfixture.interceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.lang.Nullable;
/**
* Trivial interceptor that can be introduced in a chain to display it.
*
@@ -45,14 +47,14 @@ public class NopInterceptor implements MethodInterceptor {
@Override
public boolean equals(Object other) {
if (!(other instanceof NopInterceptor)) {
return false;
}
if (this == other) {
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
return this.count == ((NopInterceptor) other).count;
if (!(obj instanceof NopInterceptor that)) {
return false;
}
return this.count == that.count;
}
@Override
@@ -1,9 +1,9 @@
/**
* Support for an HTTP service proxy created from an interface declaration.
* AspectJ-based dependency injection support.
*/
@NonNullApi
@NonNullFields
package org.springframework.web.reactive.service;
package org.springframework.beans.factory.aspectj;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -17,8 +17,8 @@
package org.springframework.cache.aspectj;
/**
* Utility to trick the compiler to throw a valid checked
* exceptions within the interceptor.
* Utility to trick the compiler to throw valid checked exceptions masked as
* runtime exceptions within the interceptor.
*
* @author Stephane Nicoll
*/
@@ -36,4 +36,5 @@ final class AnyThrow {
private static <E extends Throwable> void throwAny(Throwable e) throws E {
throw (E) e;
}
}
@@ -0,0 +1,9 @@
/**
* AspectJ-based caching support.
*/
@NonNullApi
@NonNullFields
package org.springframework.cache.aspectj;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;
@@ -0,0 +1,11 @@
/**
* AspectJ-based dependency injection support driven by the
* {@link org.springframework.beans.factory.annotation.Configurable @Configurable}
* annotation.
*/
@NonNullApi
@NonNullFields
package org.springframework.context.annotation.aspectj;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;
@@ -0,0 +1,9 @@
/**
* AspectJ-based scheduling support.
*/
@NonNullApi
@NonNullFields
package org.springframework.scheduling.aspectj;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;
@@ -0,0 +1,9 @@
/**
* AspectJ-based transaction management support.
*/
@NonNullApi
@NonNullFields
package org.springframework.transaction.aspectj;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.cache.config;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
@@ -45,16 +46,11 @@ public class TestEntity {
}
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (obj == this) {
return true;
}
if (obj == null) {
return false;
}
if (obj instanceof TestEntity) {
return ObjectUtils.nullSafeEquals(this.id, ((TestEntity) obj).id);
}
return false;
return (obj instanceof TestEntity that && ObjectUtils.nullSafeEquals(this.id, that.id));
}
}
@@ -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.
@@ -29,6 +29,7 @@ import java.time.temporal.Temporal;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -246,7 +247,8 @@ public abstract class BeanUtils {
// A single public constructor
return (Constructor<T>) ctors[0];
}
else if (ctors.length == 0){
else if (ctors.length == 0) {
// No public constructors -> check non-public
ctors = clazz.getDeclaredConstructors();
if (ctors.length == 1) {
// A single non-public constructor, e.g. from a non-public record type
@@ -790,11 +792,11 @@ public abstract class BeanUtils {
actualEditable = editable;
}
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
Set<String> ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null);
for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
if (writeMethod != null && (ignoredProps == null || !ignoredProps.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 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.
@@ -34,7 +34,7 @@ import org.springframework.lang.Nullable;
public interface ConfigurablePropertyAccessor extends PropertyAccessor, PropertyEditorRegistry, TypeConverter {
/**
* Specify a Spring 3.0 ConversionService to use for converting
* Specify a {@link ConversionService} to use for converting
* property values, as an alternative to JavaBeans PropertyEditors.
*/
void setConversionService(@Nullable ConversionService conversionService);

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