Compare commits

..

253 Commits

Author SHA1 Message Date
Spring Builds 3bea4682b7 Release v6.0.8 2023-04-13 08:23:33 +00:00
Sam Brannen be17c8d85f Disable variable assignment in SimpleEvaluationContext
This commit introduces infrastructure to differentiate between
programmatic setting of a variable in an EvaluationContext versus the
assignment of a variable within a SpEL expression using the assignment
operator (=). In addition, this commit disables variable assignment
within expressions when using the SimpleEvaluationContext.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

- Replace if-else blocks with enhanced switch statement.

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

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

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

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

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

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

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

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

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

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

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

See gh-30185

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

This commit addresses this by catching RuntimeException instead of
IllegalArgumentException.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Specifically, this commit:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

See gh-27697
Closes gh-29981
2023-02-17 12:46:26 +01:00
Spring Builds 1999c78350 Next development version (v6.0.6-SNAPSHOT) 2023-02-15 16:20:19 +00:00
1068 changed files with 11524 additions and 8532 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
+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
+11 -13
View File
@@ -1,16 +1,16 @@
plugins {
id 'io.spring.nohttp' version '0.0.11'
id 'io.freefair.aspectj' version '6.5.0.3' apply false
id 'io.freefair.aspectj' version '8.0.1' apply false
// kotlinVersion is managed in gradle.properties
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
id 'org.jetbrains.dokka' version '1.7.20'
id 'org.jetbrains.dokka' version '1.8.10'
id 'org.asciidoctor.jvm.convert' version '3.3.2' apply false
id 'org.asciidoctor.jvm.pdf' version '3.3.2' apply false
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
id 'com.github.ben-manes.versions' version '0.42.0'
id 'com.github.johnrengelman.shadow' version '7.1.2' apply false
id 'de.undercouch.download' version '5.1.0'
id 'me.champeau.jmh' version '0.6.8' apply false
id 'com.github.ben-manes.versions' version '0.46.0'
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
id 'de.undercouch.download' version '5.4.0'
id 'me.champeau.jmh' version '0.7.0' apply false
}
ext {
@@ -78,7 +78,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "10.7.0"
toolVersion = "10.9.3"
configDirectory.set(rootProject.file("src/checkstyle"))
}
@@ -105,11 +105,11 @@ configure([rootProject] + javaProjects) { project ->
testRuntimeOnly("org.junit.platform:junit-platform-suite-engine")
testRuntimeOnly("org.apache.logging.log4j:log4j-core")
testRuntimeOnly("org.apache.logging.log4j:log4j-jul")
testRuntimeOnly("org.apache.logging.log4j:log4j-slf4j-impl")
testRuntimeOnly("org.apache.logging.log4j:log4j-slf4j2-impl")
// JSR-305 only used for non-required meta-annotations
compileOnly("com.google.code.findbugs:jsr305")
testCompileOnly("com.google.code.findbugs:jsr305")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.31")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.38")
}
ext.javadocLinks = [
@@ -127,10 +127,8 @@ configure([rootProject] + javaProjects) { project ->
"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/",
@@ -26,8 +26,8 @@ import org.gradle.testretry.TestRetryTaskExtension;
* Conventions that are applied in the presence of the {@link JavaBasePlugin}. When the
* plugin is applied:
* <ul>
* <li>The {@link TestRetryPlugin Test Retry} plugins is applied so that flaky tests
* are retried 3 times when running on the CI.
* <li>The {@link TestRetryPlugin Test Retry} plugin is applied so that flaky tests
* are retried 3 times when running on the CI server.
* </ul>
*
* @author Brian Clozel
@@ -41,7 +41,7 @@ class TestConventions {
private void configureTestConventions(Project project) {
project.getTasks().withType(Test.class,
(test) -> project.getPlugins().withType(TestRetryPlugin.class, (testRetryPlugin) -> {
test -> project.getPlugins().withType(TestRetryPlugin.class, testRetryPlugin -> {
TestRetryTaskExtension testRetry = test.getExtensions().getByType(TestRetryTaskExtension.class);
testRetry.getFailOnPassedAfterRetry().set(true);
testRetry.getMaxRetries().set(isCi() ? 3 : 0);
+1 -1
View File
@@ -6,6 +6,6 @@ RUN ./setup.sh
ENV JAVA_HOME /opt/openjdk/java17
ENV JDK17 /opt/openjdk/java17
ENV JDK19 /opt/openjdk/java19
ENV JDK20 /opt/openjdk/java20
ENV PATH $JAVA_HOME/bin:$PATH
+2 -2
View File
@@ -5,8 +5,8 @@ case "$1" in
java17)
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.2+9/bellsoft-jdk19.0.2+9-linux-amd64.tar.gz"
java20)
echo "https://github.com/bell-sw/Liberica/releases/download/20%2B37/bellsoft-jdk20+37-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 java19
for jdk in java17 java20
do
JDK_URL=$( /get-jdk-url.sh $jdk )
mkdir $jdk
+8 -8
View File
@@ -127,14 +127,14 @@ resources:
access_token: ((github-ci-status-token))
branch: ((branch))
context: build
- name: repo-status-jdk19-build
- name: repo-status-jdk20-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk19-build
context: jdk20-build
- name: slack-alert
type: slack-notification
icon: slack
@@ -231,7 +231,7 @@ jobs:
"zip.type": "schema"
get_params:
threads: 8
- name: jdk19-build
- name: jdk20-build
serial: true
public: true
plan:
@@ -239,7 +239,7 @@ jobs:
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk19-build
- put: repo-status-jdk20-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
@@ -248,16 +248,16 @@ jobs:
privileged: true
timeout: ((task-timeout))
params:
TEST_TOOLCHAIN: 19
TEST_TOOLCHAIN: 20
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk19-build
- put: repo-status-jdk20-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk19-build
- put: repo-status-jdk20-build
params: { state: "success", commit: "git-repo" }
- name: build-pull-requests
serial: true
@@ -441,7 +441,7 @@ jobs:
groups:
- name: "builds"
jobs: ["build", "jdk19-build"]
jobs: ["build", "jdk20-build"]
- name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
+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,JDK19 \
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK20 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
popd > /dev/null
+1 -1
View File
@@ -28,7 +28,7 @@ javadoc {
}
dependencies {
asciidoctorExtensions "io.spring.asciidoctor.backends:spring-asciidoctor-backends:0.0.4"
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,6 +1,11 @@
// 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
:docs-site: https://docs.spring.io
// Spring Framework
:docs-spring-framework: {docs-site}/spring-framework/docs/{spring-version}
:api-spring-framework: {docs-spring-framework}/javadoc-api/org/springframework
@@ -8,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
File diff suppressed because it is too large Load Diff
@@ -16,9 +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.
* Bean definitions with instance suppliers (lambdas or method references) can't be transformed Ahead of Time (see https://github.com/spring-projects/spring-framework/issues/29555[spring-framework#29555] related issue)
* The return type of methods annotated with `@Bean` should be the most specific one in order to allow proper hint inference (typically the concrete class, not an interface).
** `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;
@@ -1300,7 +1300,7 @@ listing shows our custom `BeanDefinitionParser` implementation:
}
private static void parseChildComponents(List<Element> childElements, BeanDefinitionBuilder factory) {
ManagedList<BeanDefinition> children = new ManagedList<BeanDefinition>(childElements.size());
ManagedList<BeanDefinition> children = new ManagedList<>(childElements.size());
for (Element element : childElements) {
children.add(parseComponentElement(element));
}
@@ -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
@@ -949,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;
@@ -961,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
@@ -993,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;
@@ -1012,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
@@ -1077,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;
@@ -1093,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
@@ -2276,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
@@ -2287,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;
@@ -2310,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
@@ -2382,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;
@@ -2403,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
@@ -4214,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;
@@ -4234,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 {
@@ -7183,7 +7189,7 @@ You can add the following dependency to your file pom.xml:
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
<version>1</version>
<version>2.0.0</version>
</dependency>
----
=====
@@ -293,7 +293,7 @@ being placed in it. The following example shows how to do so:
.Java
----
class Simple {
public List<Boolean> booleanList = new ArrayList<Boolean>();
public List<Boolean> booleanList = new ArrayList<>();
}
Simple simple = new Simple();
@@ -1478,7 +1478,7 @@ show how to use the `#this` and `#root` variables:
.Java
----
// create an array of integers
List<Integer> primes = new ArrayList<Integer>();
List<Integer> primes = new ArrayList<>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));
// create parser and set variable 'primes' as the array of integers
@@ -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;
@@ -2118,7 +2122,7 @@ class Inventor(
public static String Advisors = "advisors";
public static String President = "president";
private List<Inventor> members = new ArrayList<Inventor>();
private List<Inventor> members = new ArrayList<>();
private Map officers = new HashMap();
public List getMembers() {
@@ -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
@@ -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
@@ -876,7 +878,7 @@ 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;
@@ -1223,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;
@@ -1268,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;
@@ -1302,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> {
@@ -1334,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;
@@ -1350,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
@@ -1366,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) {
@@ -1383,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();
};
}
}
----
@@ -1428,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"]
@@ -1490,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;
@@ -1526,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;
@@ -1578,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;
}
@@ -1612,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)
}
}
}
@@ -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
@@ -1028,12 +1028,30 @@ the call stack and makes a determination whether to mark the transaction for rol
In its default configuration, the Spring Framework's transaction infrastructure code
marks a transaction for rollback only in the case of runtime, unchecked exceptions.
That is, when the thrown exception is an instance or subclass of `RuntimeException`.
(`Error` instances also, by default, result in a rollback). Checked exceptions that are
thrown from a transactional method do not result in rollback in the default
configuration.
(`Error` instances also, by default, result in a rollback).
You can configure exactly which `Exception` types mark a transaction for rollback,
including checked exceptions by specifying _rollback rules_.
As of Spring Framework 5.2, the default configuration also provides support for
Vavr's `Try` method to trigger transaction rollbacks when it returns a 'Failure'.
This allows you to handle functional-style errors using Try and have the transaction
automatically rolled back in case of a failure. For more information on Vavr's Try,
refer to the [official Vavr documentation](https://www.vavr.io/vavr-docs/#_try).
Here's an example of how to use Vavr's Try with a transactional method:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Transactional
public Try<String> myTransactionalMethod() {
// If myDataAccessOperation throws an exception, it will be caught by the
// Try instance created with Try.of() and wrapped inside the Failure class
// which can be checked using the isFailure() method on the Try instance.
return Try.of(delegate::myDataAccessOperation);
}
----
Checked exceptions that are thrown from a transactional method do not result in a rollback
in the default configuration. You can configure exactly which `Exception` types mark a
transaction for rollback, including checked exceptions by specifying _rollback rules_.
.Rollback rules
[[transaction-declarative-rollback-rules]]
@@ -2028,7 +2046,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 +2083,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
@@ -4522,7 +4546,7 @@ JDBC `?` placeholders:
}
public int[] batchUpdate(final List<Actor> actors) {
List<Object[]> batch = new ArrayList<Object[]>();
List<Object[]> batch = new ArrayList<>();
for (Actor actor : actors) {
Object[] values = new Object[] {
actor.getFirstName(), actor.getLastName(), actor.getId()};
@@ -4686,7 +4710,7 @@ example uses only one configuration method (we show examples of multiple methods
}
public void add(Actor actor) {
Map<String, Object> parameters = new HashMap<String, Object>(3);
Map<String, Object> parameters = new HashMap<>(3);
parameters.put("id", actor.getId());
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
@@ -4744,7 +4768,7 @@ listing shows how it works:
}
public void add(Actor actor) {
Map<String, Object> parameters = new HashMap<String, Object>(2);
Map<String, Object> parameters = new HashMap<>(2);
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
@@ -4804,7 +4828,7 @@ You can limit the columns for an insert by specifying a list of column names wit
}
public void add(Actor actor) {
Map<String, Object> parameters = new HashMap<String, Object>(2);
Map<String, Object> parameters = new HashMap<>(2);
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
+1 -1
View File
@@ -13,7 +13,7 @@ Spring MVC Test, WebTestClient.
JDBC, R2DBC, O/R Mapping, XML Marshalling.
<<web.adoc#spring-web, Web Servlet>> :: Spring MVC, WebSocket, SockJS,
STOMP Messaging.
<<web-reactive.adoc#spring-webflux, Web Reactive>> :: Spring WebFlux, WebClient,
<<web-reactive.adoc#spring-web-reactive, Web Reactive>> :: Spring WebFlux, WebClient,
WebSocket, RSocket.
<<integration.adoc#spring-integration, Integration>> :: REST Clients, JMS, JCA, JMX,
Email, Tasks, Scheduling, Caching, Observability.
@@ -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
@@ -816,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;
@@ -827,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
@@ -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.
@@ -1655,7 +1655,7 @@ content types that a controller method produces, as the following example shows:
----
@GetMapping("/pets/{petId}", produces = ["application/json"])
@ResponseBody
fun getPet(@PathVariable String petId): Pet {
fun getPet(@PathVariable petId: String): Pet {
// ...
}
----
@@ -1704,7 +1704,7 @@ You can also use the same with request header conditions, as the following examp
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@GetMapping(path = "/pets", headers = "myHeader=myValue") // <1>
@GetMapping(path = "/pets/{petId}", headers = "myHeader=myValue") // <1>
public void findPet(@PathVariable String petId) {
// ...
}
@@ -1714,7 +1714,7 @@ You can also use the same with request header conditions, as the following examp
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/pets", headers = ["myHeader=myValue"]) // <1>
@GetMapping("/pets/{petId}", headers = ["myHeader=myValue"]) // <1>
fun findPet(@PathVariable petId: String) {
// ...
}
@@ -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`.
@@ -743,7 +743,7 @@ or to render a JSON response, as the following example shows:
@RequestMapping(path = "/error")
public Map<String, Object> handle(HttpServletRequest request) {
Map<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<>();
map.put("status", request.getAttribute("jakarta.servlet.error.status_code"));
map.put("reason", request.getAttribute("jakarta.servlet.error.message"));
return map;
@@ -1903,7 +1903,7 @@ You can also use the same with request header conditions, as the following examp
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@GetMapping(path = "/pets", headers = "myHeader=myValue") // <1>
@GetMapping(path = "/pets/{petId}", headers = "myHeader=myValue") // <1>
public void findPet(@PathVariable String petId) {
// ...
}
@@ -1913,7 +1913,7 @@ You can also use the same with request header conditions, as the following examp
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/pets", headers = ["myHeader=myValue"]) // <1>
@GetMapping("/pets/{petId}", headers = ["myHeader=myValue"]) // <1>
fun findPet(@PathVariable petId: String) {
// ...
}
@@ -4393,7 +4393,7 @@ return value with `DeferredResult`, as the following example shows:
@GetMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
DeferredResult<String> deferredResult = new DeferredResult<String>();
DeferredResult<String> deferredResult = new DeferredResult<>();
// Save the deferredResult somewhere..
return deferredResult;
}
+49 -49
View File
@@ -8,32 +8,32 @@ javaPlatform {
dependencies {
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.89.Final"))
api(platform("io.micrometer:micrometer-bom:1.10.6"))
api(platform("io.netty:netty-bom:4.1.91.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2022.0.3"))
api(platform("io.projectreactor:reactor-bom:2022.0.6"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.8"))
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.11"))
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:5.1.1"))
api(platform("org.mockito:mockito-bom:5.3.0"))
constraints {
api("com.fasterxml:aalto-xml:1.3.1")
api("com.fasterxml:aalto-xml:1.3.2")
api("com.fasterxml.woodstox:woodstox-core:6.5.0")
api("com.github.ben-manes.caffeine:caffeine:3.1.2")
api("com.github.ben-manes.caffeine:caffeine:3.1.5")
api("com.github.librepdf:openpdf:1.3.30")
api("com.google.code.findbugs:findbugs:3.0.1")
api("com.google.code.findbugs:jsr305:3.0.2")
api("com.google.code.gson:gson:2.10")
api("com.google.code.gson:gson:2.10.1")
api("com.google.protobuf:protobuf-java-util:3.21.12")
api("com.googlecode.protobuf-java-format:protobuf-java-format:1.4")
api("com.h2database:h2:2.1.214")
api("com.jayway.jsonpath:json-path:2.7.0")
api("com.rometools:rome:1.18.0")
api("com.jayway.jsonpath:json-path:2.8.0")
api("com.rometools:rome:1.19.0")
api("com.squareup.okhttp3:mockwebserver:3.14.9")
api("com.squareup.okhttp3:okhttp:3.14.9")
api("com.sun.activation:jakarta.activation:2.0.1")
@@ -41,31 +41,31 @@ dependencies {
api("com.sun.xml.bind:jaxb-core:3.0.2")
api("com.sun.xml.bind:jaxb-impl:3.0.2")
api("com.sun.xml.bind:jaxb-xjc:3.0.2")
api("com.thoughtworks.qdox:qdox:2.0.2")
api("com.thoughtworks.xstream:xstream:1.4.19")
api("com.thoughtworks.qdox:qdox:2.0.3")
api("com.thoughtworks.xstream:xstream:1.4.20")
api("commons-io:commons-io:2.11.0")
api("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.1")
api("info.picocli:picocli:4.7.0")
api("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.2")
api("info.picocli:picocli:4.7.1")
api("io.micrometer:context-propagation:1.0.0")
api("io.mockk:mockk:1.12.1")
api("io.mockk:mockk:1.13.4")
api("io.projectreactor.netty:reactor-netty5-http:2.0.0-M3")
api("io.projectreactor.tools:blockhound:1.0.6.RELEASE")
api("io.projectreactor.tools:blockhound:1.0.7.RELEASE")
api("io.r2dbc:r2dbc-h2:1.0.0.RELEASE")
api("io.r2dbc:r2dbc-spi-test:1.0.0.RELEASE")
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.3.Final")
api("io.undertow:undertow-servlet:2.3.3.Final")
api("io.undertow:undertow-websockets-jsr:2.3.3.Final")
api("io.reactivex.rxjava3:rxjava:3.1.6")
api("io.smallrye.reactive:mutiny:1.9.0")
api("io.undertow:undertow-core:2.3.5.Final")
api("io.undertow:undertow-servlet:2.3.5.Final")
api("io.undertow:undertow-websockets-jsr:2.3.5.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")
api("jakarta.ejb:jakarta.ejb-api:4.0.0")
api("jakarta.ejb:jakarta.ejb-api:4.0.1")
api("jakarta.el:jakarta.el-api:4.0.0")
api("jakarta.enterprise.concurrent:jakarta.enterprise.concurrent-api:2.0.0")
api("jakarta.faces:jakarta.faces-api:3.0.0")
api("jakarta.inject:jakarta.inject-api:2.0.0")
api("jakarta.inject:jakarta.inject-api:2.0.1")
api("jakarta.inject:jakarta.inject-tck:2.0.1")
api("jakarta.interceptor:jakarta.interceptor-api:2.0.0")
api("jakarta.jms:jakarta.jms-api:3.0.0")
@@ -75,7 +75,7 @@ dependencies {
api("jakarta.persistence:jakarta.persistence-api:3.0.0")
api("jakarta.resource:jakarta.resource-api:2.0.0")
api("jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:3.0.0")
api("jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.1.0")
api("jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.1.1")
api("jakarta.servlet:jakarta.servlet-api:6.0.0")
api("jakarta.transaction:jakarta.transaction-api:2.0.1")
api("jakarta.validation:jakarta.validation-api:3.0.2")
@@ -89,9 +89,9 @@ dependencies {
api("net.sf.jopt-simple:jopt-simple:5.0.4")
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")
api("org.apache.activemq:activemq-stomp:5.16.2")
api("org.apache.activemq:activemq-broker:5.17.2")
api("org.apache.activemq:activemq-kahadb-store:5.17.2")
api("org.apache.activemq:activemq-stomp:5.17.2")
api("org.apache.commons:commons-pool2:2.9.0")
api("org.apache.derby:derby:10.16.1.1")
api("org.apache.derby:derbyclient:10.16.1.1")
@@ -99,45 +99,45 @@ dependencies {
api("org.apache.httpcomponents.client5:httpclient5:5.2.1")
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.5")
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.5")
api("org.apache.tomcat:tomcat-util:10.1.5")
api("org.apache.tomcat:tomcat-websocket:10.1.5")
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.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.19")
api("org.aspectj:aspectjtools:1.9.19")
api("org.aspectj:aspectjweaver:1.9.19")
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")
api("org.dom4j:dom4j:2.1.3")
api("org.eclipse.jetty:jetty-reactive-httpclient:3.0.7")
api("org.awaitility:awaitility:4.2.0")
api("org.bouncycastle:bcpkix-jdk18on:1.72")
api("org.codehaus.jettison:jettison:1.5.4")
api("org.dom4j:dom4j:2.1.4")
api("org.eclipse.jetty:jetty-reactive-httpclient:3.0.8")
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.3")
api("org.eclipse:yasson:2.0.4")
api("org.ehcache:ehcache:3.4.0")
api("org.ehcache:ehcache:3.10.8")
api("org.ehcache:jcache:1.0.1")
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")
api("org.glassfish.tyrus:tyrus-container-servlet:2.0.1")
api("org.glassfish.tyrus:tyrus-container-servlet:2.1.3")
api("org.glassfish:jakarta.el:4.0.2")
api("org.graalvm.sdk:graal-sdk:22.3.0")
api("org.graalvm.sdk:graal-sdk:22.3.1")
api("org.hamcrest:hamcrest:2.2")
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")
api("org.jruby:jruby:9.4.0.0")
api("org.jruby:jruby:9.4.2.0")
api("org.junit.support:testng-engine:1.0.4")
api("org.mozilla:rhino:1.7.11")
api("org.mozilla:rhino:1.7.14")
api("org.ogce:xpp3:1.1.6")
api("org.python:jython-standalone:2.7.1")
api("org.python:jython-standalone:2.7.3")
api("org.quartz-scheduler:quartz:2.3.2")
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.skyscreamer:jsonassert:1.5.1")
api("org.slf4j:slf4j-api:2.0.7")
api("org.testng:testng:7.7.1")
api("org.webjars:underscorejs:1.8.3")
api("org.webjars:webjars-locator-core:0.52")
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.0.5
version=6.0.8
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
+6 -5
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=19"} will use:
* {@code "./gradlew check -PmainToolchain=17 -PtestToolchain=20"} will use:
* <ul>
* <li>a JDK17 toolchain for compiling the main SourceSet
* <li>a JDK19 toolchain for compiling and running the test SourceSet
* <li>a JDK20 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 JDK19
* /opt/openjdk/java18
* $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK17,JDK19 check
* $ echo JDK20
* /opt/openjdk/java20
* $ ./gradlew -Porg.gradle.java.installations.fromEnv=JDK17,JDK20 check
* }
*
* @author Brian Clozel
@@ -81,6 +81,7 @@ plugins.withType(JavaPlugin) {
javaLauncher = javaToolchains.launcherFor {
languageVersion = testLanguageVersion
}
jvmArgs += ['-Djava.locale.providers=COMPAT']
}
}
}
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+2 -2
View File
@@ -144,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -152,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -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.
@@ -99,7 +99,7 @@ class AopNamespaceHandlerScopeIntegrationTests {
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(oldRequest));
assertThat(requestScoped.getName()).isEqualTo(bram);
assertThat(((Advised) requestScoped).getAdvisors().length > 0).as("Should have advisors").isTrue();
assertThat(((Advised) requestScoped).getAdvisors()).as("Should have advisors").isNotEmpty();
}
@Test
@@ -131,7 +131,7 @@ class AopNamespaceHandlerScopeIntegrationTests {
request.setSession(oldSession);
assertThat(sessionScoped.getName()).isEqualTo(bram);
assertThat(((Advised) sessionScoped).getAdvisors().length > 0).as("Should have advisors").isTrue();
assertThat(((Advised) sessionScoped).getAdvisors()).as("Should have advisors").isNotEmpty();
}
}
@@ -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.
@@ -52,25 +52,25 @@ class ComponentBeanDefinitionParserTests {
@Test
void testBionicBasic() {
Component cp = getBionicFamily();
assertThat("Bionic-1").isEqualTo(cp.getName());
assertThat(cp.getName()).isEqualTo("Bionic-1");
}
@Test
void testBionicFirstLevelChildren() {
Component cp = getBionicFamily();
List<Component> components = cp.getComponents();
assertThat(2).isEqualTo(components.size());
assertThat("Mother-1").isEqualTo(components.get(0).getName());
assertThat("Rock-1").isEqualTo(components.get(1).getName());
assertThat(components).hasSize(2);
assertThat(components.get(0).getName()).isEqualTo("Mother-1");
assertThat(components.get(1).getName()).isEqualTo("Rock-1");
}
@Test
void testBionicSecondLevelChildren() {
Component cp = getBionicFamily();
List<Component> components = cp.getComponents().get(0).getComponents();
assertThat(2).isEqualTo(components.size());
assertThat("Karate-1").isEqualTo(components.get(0).getName());
assertThat("Sport-1").isEqualTo(components.get(1).getName());
assertThat(components).hasSize(2);
assertThat(components.get(0).getName()).isEqualTo("Karate-1");
assertThat(components.get(1).getName()).isEqualTo("Sport-1");
}
private Component getBionicFamily() {
+1 -1
View File
@@ -7,7 +7,7 @@ pluginManagement {
}
plugins {
id "com.gradle.enterprise" version "3.12.3"
id "com.gradle.enterprise" version "3.12.6"
id "io.spring.ge.conventions" version "0.0.13"
}
@@ -248,20 +248,26 @@ 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 '" +
@@ -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
*/
@@ -309,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() {
@@ -348,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);
@@ -374,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.
@@ -387,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.
@@ -431,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) {
@@ -452,7 +450,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
}
/*
/**
* If the token starts meets Java identifier conventions, it's in.
*/
@Nullable
@@ -488,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<>();
@@ -520,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++) {
@@ -596,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.
*/
@@ -649,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...
@@ -696,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.
*/
@@ -724,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.
*/
@@ -741,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)));
}
@@ -121,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;
@@ -156,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"};
@@ -171,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;
@@ -179,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 : "");
}
@@ -192,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;
@@ -200,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;
}
@@ -239,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-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-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.
@@ -120,6 +120,11 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
if (classLoader == null || classLoader.getParent() == null) {
// JDK bootstrap loader or platform loader suggested ->
// use higher-level loader which can see Spring infrastructure classes
classLoader = getClass().getClassLoader();
}
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
@@ -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.
@@ -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-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.
@@ -70,7 +70,7 @@ public class MethodInvocationProceedingJoinPointTests {
AtomicInteger depth = new AtomicInteger();
pf.addAdvice((MethodBeforeAdvice) (method, args, target) -> {
JoinPoint jp = AbstractAspectJAdvice.currentJoinPoint();
assertThat(jp.toString().contains(method.getName())).as("Method named in toString").isTrue();
assertThat(jp.toString()).as("Method named in toString").contains(method.getName());
// Ensure that these don't cause problems
jp.toShortString();
jp.toLongString();
@@ -319,10 +319,10 @@ abstract class AbstractAspectJAdvisorFactoryTests {
@Test
void introductionOnTargetNotImplementingInterface() {
NotLockable notLockableTarget = new NotLockable();
assertThat(notLockableTarget instanceof Lockable).isFalse();
assertThat(notLockableTarget).isNotInstanceOf(Lockable.class);
NotLockable notLockable1 = createProxy(notLockableTarget, NotLockable.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")));
assertThat(notLockable1 instanceof Lockable).isTrue();
assertThat(notLockable1).isInstanceOf(Lockable.class);
Lockable lockable = (Lockable) notLockable1;
assertThat(lockable.locked()).isFalse();
lockable.lock();
@@ -331,7 +331,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
NotLockable notLockable2Target = new NotLockable();
NotLockable notLockable2 = createProxy(notLockable2Target, NotLockable.class,
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")));
assertThat(notLockable2 instanceof Lockable).isTrue();
assertThat(notLockable2).isInstanceOf(Lockable.class);
Lockable lockable2 = (Lockable) notLockable2;
assertThat(lockable2.locked()).isFalse();
notLockable2.setIntValue(1);
@@ -345,7 +345,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
assertThat(AopUtils.findAdvisorsThatCanApply(
getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeLockable(), "someBean")),
CannotBeUnlocked.class).isEmpty()).isTrue();
CannotBeUnlocked.class)).isEmpty();
assertThat(AopUtils.findAdvisorsThatCanApply(getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new MakeLockable(),"someBean")), NotLockable.class)).hasSize(2);
}
@@ -373,7 +373,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
AopUtils.findAdvisorsThatCanApply(
getAdvisorFactory().getAdvisors(aspectInstanceFactory(new MakeLockable(), "someBean")),
List.class));
assertThat(proxy instanceof Lockable).as("Type pattern must have excluded mixin").isFalse();
assertThat(proxy).as("Type pattern must have excluded mixin").isNotInstanceOf(Lockable.class);
}
@Test
@@ -430,7 +430,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
UnsupportedOperationException expectedException = new UnsupportedOperationException();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
assertThat(advisors.size()).as("One advice method was found").isEqualTo(1);
assertThat(advisors).as("One advice method was found").hasSize(1);
ITestBean itb = createProxy(target, ITestBean.class, advisors);
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(itb::getAge);
}
@@ -443,7 +443,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
RemoteException expectedException = new RemoteException();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean"));
assertThat(advisors.size()).as("One advice method was found").isEqualTo(1);
assertThat(advisors).as("One advice method was found").hasSize(1);
ITestBean itb = createProxy(target, ITestBean.class, advisors);
assertThatExceptionOfType(UndeclaredThrowableException.class)
.isThrownBy(itb::getAge)
@@ -456,7 +456,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
TwoAdviceAspect twoAdviceAspect = new TwoAdviceAspect();
List<Advisor> advisors = getAdvisorFactory().getAdvisors(
aspectInstanceFactory(twoAdviceAspect, "someBean"));
assertThat(advisors.size()).as("Two advice methods found").isEqualTo(2);
assertThat(advisors).as("Two advice methods found").hasSize(2);
ITestBean itb = createProxy(target, ITestBean.class, advisors);
itb.setName("");
assertThat(itb.getAge()).isEqualTo(0);
@@ -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-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,10 @@
package org.springframework.aop.framework;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.accessibility.Accessible;
@@ -194,11 +197,11 @@ public class ProxyFactoryTests {
TestBeanSubclass raw = new TestBeanSubclass();
ProxyFactory factory = new ProxyFactory(raw);
//System.out.println("Proxied interfaces are " + StringUtils.arrayToDelimitedString(factory.getProxiedInterfaces(), ","));
assertThat(factory.getProxiedInterfaces().length).as("Found correct number of interfaces").isEqualTo(5);
assertThat(factory.getProxiedInterfaces()).as("Found correct number of interfaces").hasSize(5);
ITestBean tb = (ITestBean) factory.getProxy();
assertThat(tb).as("Picked up secondary interface").isInstanceOf(IOther.class);
raw.setAge(25);
assertThat(tb.getAge() == raw.getAge()).isTrue();
assertThat(tb.getAge()).isEqualTo(raw.getAge());
long t = 555555L;
TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor(t);
@@ -208,10 +211,10 @@ public class ProxyFactoryTests {
factory.addAdvisor(0, new DefaultIntroductionAdvisor(ti, TimeStamped.class));
Class<?>[] newProxiedInterfaces = factory.getProxiedInterfaces();
assertThat(newProxiedInterfaces.length).as("Advisor proxies one more interface after introduction").isEqualTo(oldProxiedInterfaces.length + 1);
assertThat(newProxiedInterfaces).as("Advisor proxies one more interface after introduction").hasSize(oldProxiedInterfaces.length + 1);
TimeStamped ts = (TimeStamped) factory.getProxy();
assertThat(ts.getTimeStamp() == t).isTrue();
assertThat(ts.getTimeStamp()).isEqualTo(t);
// Shouldn't fail;
((IOther) ts).absquatulate();
}
@@ -231,13 +234,13 @@ public class ProxyFactoryTests {
factory.addAdvice(0, di);
assertThat(factory.getProxy()).isInstanceOf(ITestBean.class);
assertThat(factory.adviceIncluded(di)).isTrue();
assertThat(!factory.adviceIncluded(diUnused)).isTrue();
assertThat(factory.countAdvicesOfType(NopInterceptor.class) == 1).isTrue();
assertThat(factory.countAdvicesOfType(MyInterceptor.class) == 0).isTrue();
assertThat(factory.adviceIncluded(diUnused)).isFalse();
assertThat(factory.countAdvicesOfType(NopInterceptor.class)).isEqualTo(1);
assertThat(factory.countAdvicesOfType(MyInterceptor.class)).isEqualTo(0);
factory.addAdvice(0, diUnused);
assertThat(factory.adviceIncluded(diUnused)).isTrue();
assertThat(factory.countAdvicesOfType(NopInterceptor.class) == 2).isTrue();
assertThat(factory.countAdvicesOfType(NopInterceptor.class)).isEqualTo(2);
}
@Test
@@ -257,7 +260,8 @@ public class ProxyFactoryTests {
public void testCanAddAndRemoveAspectInterfacesOnSingleton() {
ProxyFactory config = new ProxyFactory(new TestBean());
assertThat(config.getProxy() instanceof TimeStamped).as("Shouldn't implement TimeStamped before manipulation").isFalse();
assertThat(config.getProxy()).as("Shouldn't implement TimeStamped before manipulation")
.isNotInstanceOf(TimeStamped.class);
long time = 666L;
TimestampIntroductionInterceptor ti = new TimestampIntroductionInterceptor();
@@ -267,26 +271,26 @@ public class ProxyFactoryTests {
int oldCount = config.getAdvisors().length;
config.addAdvisor(0, new DefaultIntroductionAdvisor(ti, TimeStamped.class));
assertThat(config.getAdvisors().length == oldCount + 1).isTrue();
assertThat(config.getAdvisors()).hasSize(oldCount + 1);
TimeStamped ts = (TimeStamped) config.getProxy();
assertThat(ts.getTimeStamp() == time).isTrue();
assertThat(ts.getTimeStamp()).isEqualTo(time);
// Can remove
config.removeAdvice(ti);
assertThat(config.getAdvisors().length == oldCount).isTrue();
assertThat(config.getAdvisors()).hasSize(oldCount);
assertThatRuntimeException()
.as("Existing object won't implement this interface any more")
.isThrownBy(ts::getTimeStamp); // Existing reference will fail
assertThat(config.getProxy() instanceof TimeStamped).as("Should no longer implement TimeStamped").isFalse();
assertThat(config.getProxy()).as("Should no longer implement TimeStamped").isNotInstanceOf(TimeStamped.class);
// Now check non-effect of removing interceptor that isn't there
config.removeAdvice(new DebugInterceptor());
assertThat(config.getAdvisors().length == oldCount).isTrue();
assertThat(config.getAdvisors()).hasSize(oldCount);
ITestBean it = (ITestBean) ts;
DebugInterceptor debugInterceptor = new DebugInterceptor();
@@ -296,7 +300,7 @@ public class ProxyFactoryTests {
config.removeAdvice(debugInterceptor);
it.getSpouse();
// not invoked again
assertThat(debugInterceptor.getCount() == 1).isTrue();
assertThat(debugInterceptor.getCount()).isEqualTo(1);
}
@Test
@@ -305,13 +309,13 @@ public class ProxyFactoryTests {
pf.setTargetClass(ITestBean.class);
Object proxy = pf.getProxy();
assertThat(AopUtils.isJdkDynamicProxy(proxy)).as("Proxy is a JDK proxy").isTrue();
assertThat(proxy instanceof ITestBean).isTrue();
assertThat(proxy).isInstanceOf(ITestBean.class);
assertThat(AopProxyUtils.ultimateTargetClass(proxy)).isEqualTo(ITestBean.class);
ProxyFactory pf2 = new ProxyFactory(proxy);
Object proxy2 = pf2.getProxy();
assertThat(AopUtils.isJdkDynamicProxy(proxy2)).as("Proxy is a JDK proxy").isTrue();
assertThat(proxy2 instanceof ITestBean).isTrue();
assertThat(proxy2).isInstanceOf(ITestBean.class);
assertThat(AopProxyUtils.ultimateTargetClass(proxy2)).isEqualTo(ITestBean.class);
}
@@ -321,14 +325,14 @@ public class ProxyFactoryTests {
pf.setTargetClass(TestBean.class);
Object proxy = pf.getProxy();
assertThat(AopUtils.isCglibProxy(proxy)).as("Proxy is a CGLIB proxy").isTrue();
assertThat(proxy instanceof TestBean).isTrue();
assertThat(proxy).isInstanceOf(TestBean.class);
assertThat(AopProxyUtils.ultimateTargetClass(proxy)).isEqualTo(TestBean.class);
ProxyFactory pf2 = new ProxyFactory(proxy);
pf2.setProxyTargetClass(true);
Object proxy2 = pf2.getProxy();
assertThat(AopUtils.isCglibProxy(proxy2)).as("Proxy is a CGLIB proxy").isTrue();
assertThat(proxy2 instanceof TestBean).isTrue();
assertThat(proxy2).isInstanceOf(TestBean.class);
assertThat(AopProxyUtils.ultimateTargetClass(proxy2)).isEqualTo(TestBean.class);
}
@@ -338,8 +342,8 @@ public class ProxyFactoryTests {
JFrame frame = new JFrame();
ProxyFactory proxyFactory = new ProxyFactory(frame);
Object proxy = proxyFactory.getProxy();
assertThat(proxy instanceof RootPaneContainer).isTrue();
assertThat(proxy instanceof Accessible).isTrue();
assertThat(proxy).isInstanceOf(RootPaneContainer.class);
assertThat(proxy).isInstanceOf(Accessible.class);
}
@Test
@@ -380,6 +384,40 @@ public class ProxyFactoryTests {
assertThat(proxy.getName()).isEqualTo("tb");
}
@Test
public void testCharSequenceProxy() {
CharSequence target = "test";
ProxyFactory pf = new ProxyFactory(target);
ClassLoader cl = target.getClass().getClassLoader();
assertThat(((CharSequence) pf.getProxy(cl)).toString()).isEqualTo(target);
}
@Test
public void testDateProxy() {
Date target = new Date();
ProxyFactory pf = new ProxyFactory(target);
pf.setProxyTargetClass(true);
ClassLoader cl = target.getClass().getClassLoader();
assertThat(((Date) pf.getProxy(cl)).getTime()).isEqualTo(target.getTime());
}
@Test
public void testJdbcSavepointProxy() throws SQLException {
Savepoint target = new Savepoint() {
@Override
public int getSavepointId() throws SQLException {
return 1;
}
@Override
public String getSavepointName() throws SQLException {
return "sp";
}
};
ProxyFactory pf = new ProxyFactory(target);
ClassLoader cl = Savepoint.class.getClassLoader();
assertThat(((Savepoint) pf.getProxy(cl)).getSavepointName()).isEqualTo("sp");
}
@Order(2)
public static class A implements Runnable {
@@ -391,7 +429,7 @@ public class ProxyFactoryTests {
@Order(1)
public static class B implements Runnable{
public static class B implements Runnable {
@Override
public void run() {
@@ -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.
@@ -26,7 +26,7 @@ class InvocationCheckExposedInvocationTestBean extends ExposedInvocationTestBean
@Override
protected void assertions(MethodInvocation invocation) {
assertThat(invocation.getThis() == this).isTrue();
assertThat(invocation.getThis()).isSameAs(this);
assertThat(ITestBean.class.isAssignableFrom(invocation.getMethod().getDeclaringClass())).as("Invocation should be on ITestBean: " + invocation.getMethod()).isTrue();
}
}
@@ -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.
@@ -128,7 +128,7 @@ class ScopedProxyBeanRegistrationAotProcessorTests {
this.beanFactory.registerBeanDefinition("test", scopedBean);
compile((freshBeanFactory, compiled) -> {
Object bean = freshBeanFactory.getBean("test");
assertThat(bean).isNotNull().isInstanceOf(NumberHolder.class).isInstanceOf(AopInfrastructureBean.class);
assertThat(bean).isInstanceOf(NumberHolder.class).isInstanceOf(AopInfrastructureBean.class);
});
}
@@ -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.
@@ -142,7 +142,7 @@ public class ComposablePointcutTests {
pc1.intersection(GETTER_METHOD_MATCHER);
assertThat(pc1.equals(pc2)).isFalse();
assertThat(pc1.hashCode() == pc2.hashCode()).isFalse();
assertThat(pc1.hashCode()).isNotEqualTo(pc2.hashCode());
pc2.intersection(GETTER_METHOD_MATCHER);
@@ -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.
@@ -97,7 +97,7 @@ public class ControlFlowPointcutTests {
assertThat(new ControlFlowPointcut(One.class, "getAge").equals(new ControlFlowPointcut(One.class))).isFalse();
assertThat(new ControlFlowPointcut(One.class).hashCode()).isEqualTo(new ControlFlowPointcut(One.class).hashCode());
assertThat(new ControlFlowPointcut(One.class, "getAge").hashCode()).isEqualTo(new ControlFlowPointcut(One.class, "getAge").hashCode());
assertThat(new ControlFlowPointcut(One.class, "getAge").hashCode() == new ControlFlowPointcut(One.class).hashCode()).isFalse();
assertThat(new ControlFlowPointcut(One.class, "getAge").hashCode()).isNotEqualTo(new ControlFlowPointcut(One.class).hashCode());
}
@Test
@@ -56,7 +56,7 @@ class DelegatingIntroductionInterceptorTests {
@Test
void testIntroductionInterceptorWithDelegation() throws Exception {
TestBean raw = new TestBean();
assertThat(! (raw instanceof TimeStamped)).isTrue();
assertThat(raw).isNotInstanceOf(TimeStamped.class);
ProxyFactory factory = new ProxyFactory(raw);
TimeStamped ts = mock();
@@ -66,13 +66,13 @@ class DelegatingIntroductionInterceptorTests {
factory.addAdvisor(0, new DefaultIntroductionAdvisor(new DelegatingIntroductionInterceptor(ts)));
TimeStamped tsp = (TimeStamped) factory.getProxy();
assertThat(tsp.getTimeStamp() == timestamp).isTrue();
assertThat(tsp.getTimeStamp()).isEqualTo(timestamp);
}
@Test
void testIntroductionInterceptorWithInterfaceHierarchy() throws Exception {
TestBean raw = new TestBean();
assertThat(! (raw instanceof SubTimeStamped)).isTrue();
assertThat(raw).isNotInstanceOf(SubTimeStamped.class);
ProxyFactory factory = new ProxyFactory(raw);
SubTimeStamped ts = mock();
@@ -82,13 +82,13 @@ class DelegatingIntroductionInterceptorTests {
factory.addAdvisor(0, new DefaultIntroductionAdvisor(new DelegatingIntroductionInterceptor(ts), SubTimeStamped.class));
SubTimeStamped tsp = (SubTimeStamped) factory.getProxy();
assertThat(tsp.getTimeStamp() == timestamp).isTrue();
assertThat(tsp.getTimeStamp()).isEqualTo(timestamp);
}
@Test
void testIntroductionInterceptorWithSuperInterface() throws Exception {
TestBean raw = new TestBean();
assertThat(! (raw instanceof TimeStamped)).isTrue();
assertThat(raw).isNotInstanceOf(TimeStamped.class);
ProxyFactory factory = new ProxyFactory(raw);
SubTimeStamped ts = mock();
@@ -98,8 +98,8 @@ class DelegatingIntroductionInterceptorTests {
factory.addAdvisor(0, new DefaultIntroductionAdvisor(new DelegatingIntroductionInterceptor(ts), TimeStamped.class));
TimeStamped tsp = (TimeStamped) factory.getProxy();
assertThat(!(tsp instanceof SubTimeStamped)).isTrue();
assertThat(tsp.getTimeStamp() == timestamp).isTrue();
assertThat(tsp).isNotInstanceOf(SubTimeStamped.class);
assertThat(tsp.getTimeStamp()).isEqualTo(timestamp);
}
@Test
@@ -125,7 +125,7 @@ class DelegatingIntroductionInterceptorTests {
//assertTrue(Arrays.binarySearch(pf.getProxiedInterfaces(), TimeStamped.class) != -1);
TimeStamped ts = (TimeStamped) pf.getProxy();
assertThat(ts.getTimeStamp() == t).isTrue();
assertThat(ts.getTimeStamp()).isEqualTo(t);
((ITester) ts).foo();
((ITestBean) ts).getAge();
@@ -160,10 +160,10 @@ class DelegatingIntroductionInterceptorTests {
assertThat(ts).isInstanceOf(TimeStamped.class);
// Shouldn't proxy framework interfaces
assertThat(!(ts instanceof MethodInterceptor)).isTrue();
assertThat(!(ts instanceof IntroductionInterceptor)).isTrue();
assertThat(ts).isNotInstanceOf(MethodInterceptor.class);
assertThat(ts).isNotInstanceOf(IntroductionInterceptor.class);
assertThat(ts.getTimeStamp() == t).isTrue();
assertThat(ts.getTimeStamp()).isEqualTo(t);
((ITester) ts).foo();
((ITestBean) ts).getAge();
@@ -174,14 +174,14 @@ class DelegatingIntroductionInterceptorTests {
pf = new ProxyFactory(target);
pf.addAdvisor(0, new DefaultIntroductionAdvisor(ii));
Object o = pf.getProxy();
assertThat(!(o instanceof TimeStamped)).isTrue();
assertThat(o).isNotInstanceOf(TimeStamped.class);
}
@SuppressWarnings("serial")
@Test
void testIntroductionInterceptorDoesntReplaceToString() throws Exception {
TestBean raw = new TestBean();
assertThat(! (raw instanceof TimeStamped)).isTrue();
assertThat(raw).isNotInstanceOf(TimeStamped.class);
ProxyFactory factory = new ProxyFactory(raw);
TimeStamped ts = new SerializableTimeStamped(0);
@@ -266,7 +266,7 @@ class DelegatingIntroductionInterceptorTests {
TimeStamped ts = (TimeStamped) pf.getProxy();
// From introduction interceptor, not target
assertThat(ts.getTimeStamp() == t).isTrue();
assertThat(ts.getTimeStamp()).isEqualTo(t);
}
@@ -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.
@@ -129,7 +129,7 @@ public class NameMatchMethodPointcutTests {
pc1.setMappedName(foo);
assertThat(pc1.equals(pc2)).isFalse();
assertThat(pc1.hashCode() != pc2.hashCode()).isTrue();
assertThat(pc1.hashCode()).isNotEqualTo(pc2.hashCode());
pc2.setMappedName(foo);
assertThat(pc2).isEqualTo(pc1);
@@ -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-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.
@@ -543,7 +543,7 @@ public abstract class AbstractCacheAnnotationTests {
Object r1 = service.multiConditionalCacheAndEvict(key);
Object r3 = service.multiConditionalCacheAndEvict(key);
assertThat(!r1.equals(r3)).isTrue();
assertThat(r1.equals(r3)).isFalse();
assertThat(primary.get(key)).isNull();
Object key2 = 3;
@@ -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.
@@ -92,7 +92,7 @@ public class AspectJEnableCachingIsolatedTests {
load(MultiCacheManagerConfig.class);
}
catch (IllegalStateException ex) {
assertThat(ex.getMessage().contains("bean of type CacheManager")).isTrue();
assertThat(ex.getMessage()).contains("bean of type CacheManager");
}
}
@@ -107,7 +107,7 @@ public class AspectJEnableCachingIsolatedTests {
load(MultiCacheManagerConfigurer.class, EnableCachingConfig.class);
}
catch (IllegalStateException ex) {
assertThat(ex.getMessage().contains("implementations of CachingConfigurer")).isTrue();
assertThat(ex.getMessage()).contains("implementations of CachingConfigurer");
}
}
@@ -117,7 +117,7 @@ public class AspectJEnableCachingIsolatedTests {
load(EmptyConfig.class);
}
catch (IllegalStateException ex) {
assertThat(ex.getMessage().contains("no bean of type CacheManager")).isTrue();
assertThat(ex.getMessage()).contains("no bean of type CacheManager");
}
}
@@ -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-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.
@@ -88,8 +88,9 @@ abstract class PropertyDescriptorUtils {
BasicPropertyDescriptor pd = pdMap.get(propertyName);
if (pd != null) {
if (setter) {
if (pd.getWriteMethod() == null ||
pd.getWriteMethod().getParameterTypes()[0].isAssignableFrom(method.getParameterTypes()[0])) {
Method writeMethod = pd.getWriteMethod();
if (writeMethod == null ||
writeMethod.getParameterTypes()[0].isAssignableFrom(method.getParameterTypes()[0])) {
pd.setWriteMethod(method);
}
else {
@@ -97,8 +98,9 @@ abstract class PropertyDescriptorUtils {
}
}
else {
if (pd.getReadMethod() == null ||
(pd.getReadMethod().getReturnType() == method.getReturnType() && method.getName().startsWith("is"))) {
Method readMethod = pd.getReadMethod();
if (readMethod == null ||
(readMethod.getReturnType() == method.getReturnType() && method.getName().startsWith("is"))) {
pd.setReadMethod(method);
}
}
@@ -280,6 +280,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
}
@Override
@Nullable
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
Class<?> beanClass = registeredBean.getBeanClass();
String beanName = registeredBean.getBeanName();
@@ -323,10 +324,10 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
checkLookupMethods(beanClass, beanName);
// Pick up subclass with fresh lookup method override from above
if (this.beanFactory instanceof AbstractAutowireCapableBeanFactory aacbf) {
if (this.beanFactory instanceof AbstractAutowireCapableBeanFactory aacBeanFactory) {
RootBeanDefinition mbd = (RootBeanDefinition) this.beanFactory.getMergedBeanDefinition(beanName);
if (mbd.getFactoryMethodName() == null && mbd.hasBeanClass()) {
return aacbf.getInstantiationStrategy().getActualBeanClass(mbd, beanName, this.beanFactory);
return aacBeanFactory.getInstantiationStrategy().getActualBeanClass(mbd, beanName, aacBeanFactory);
}
}
return beanClass;
@@ -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.
@@ -157,6 +157,7 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
}
@Override
@Nullable
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition();
beanDefinition.resolveDestroyMethodIfNecessary();
@@ -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.util.stream.Stream;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
@@ -31,7 +32,7 @@ import org.springframework.util.ClassUtils;
class JakartaAnnotationsRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
if (ClassUtils.isPresent("jakarta.inject.Inject", classLoader)) {
Stream.of("jakarta.inject.Inject", "jakarta.inject.Qualifier").forEach(annotationType ->
hints.reflection().registerType(ClassUtils.resolveClassName(annotationType, classLoader)));
@@ -25,6 +25,7 @@ import java.util.Set;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
@@ -240,10 +241,11 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
}
}
if (targetAnnotation == null) {
BeanFactory beanFactory = getBeanFactory();
// Look for matching annotation on the target class
if (getBeanFactory() != null) {
if (beanFactory != null) {
try {
Class<?> beanType = getBeanFactory().getType(bdHolder.getBeanName());
Class<?> beanType = beanFactory.getType(bdHolder.getBeanName());
if (beanType != null) {
targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type);
}
@@ -138,7 +138,7 @@ class BeanDefinitionMethodGenerator {
ClassName topLevelClassName = target.topLevelClassName();
GeneratedClass generatedClass = generationContext.getGeneratedClasses()
.getOrAddForFeatureComponent("BeanDefinitions", topLevelClassName, type -> {
type.addJavadoc("Bean definitions for {@link $T}", topLevelClassName);
type.addJavadoc("Bean definitions for {@link $T}.", topLevelClassName);
type.addModifiers(Modifier.PUBLIC);
});
@@ -159,7 +159,7 @@ class BeanDefinitionMethodGenerator {
private static GeneratedClass createInnerClass(GeneratedClass generatedClass, String name, ClassName target) {
return generatedClass.getOrAdd(name, type -> {
type.addJavadoc("Bean definitions for {@link $T}", target);
type.addJavadoc("Bean definitions for {@link $T}.", target);
type.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
});
}
@@ -186,7 +186,7 @@ class BeanDefinitionMethodGenerator {
this.aotContributions.forEach(aotContribution -> aotContribution.applyTo(generationContext, codeGenerator));
return generatedMethods.add("getBeanDefinition", method -> {
method.addJavadoc("Get the $L definition for '$L'",
method.addJavadoc("Get the $L definition for '$L'.",
(!this.registeredBean.isInnerBean()) ? "bean" : "inner-bean",
getName());
method.addModifiers(modifier, Modifier.STATIC);
@@ -27,9 +27,9 @@ import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.aot.hint.ExecutableMode;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.InjectionPoint;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -97,11 +97,13 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
private BeanInstanceSupplier(ExecutableLookup lookup,
@Nullable ThrowingBiFunction<RegisteredBean, AutowiredArguments, T> generator,
@Nullable String[] shortcuts) {
this.lookup = lookup;
this.generator = generator;
this.shortcuts = shortcuts;
}
/**
* Create a {@link BeanInstanceSupplier} that resolves
* arguments for the specified bean constructor.
@@ -109,9 +111,7 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
* @param parameterTypes the constructor parameter types
* @return a new {@link BeanInstanceSupplier} instance
*/
public static <T> BeanInstanceSupplier<T> forConstructor(
Class<?>... parameterTypes) {
public static <T> BeanInstanceSupplier<T> forConstructor(Class<?>... parameterTypes) {
Assert.notNull(parameterTypes, "'parameterTypes' must not be null");
Assert.noNullElements(parameterTypes, "'parameterTypes' must not contain null elements");
return new BeanInstanceSupplier<>(new ConstructorLookup(parameterTypes), null, null);
@@ -149,11 +149,11 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
* @param generator a {@link ThrowingBiFunction} that uses the
* {@link RegisteredBean} and resolved {@link AutowiredArguments} to
* instantiate the underlying bean
* @return a new {@link BeanInstanceSupplier} instance with the specified
* generator
* @return a new {@link BeanInstanceSupplier} instance with the specified generator
*/
public BeanInstanceSupplier<T> withGenerator(
ThrowingBiFunction<RegisteredBean, AutowiredArguments, T> generator) {
Assert.notNull(generator, "'generator' must not be null");
return new BeanInstanceSupplier<>(this.lookup, generator, this.shortcuts);
}
@@ -163,11 +163,9 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
* {@code generator} function to instantiate the underlying bean.
* @param generator a {@link ThrowingFunction} that uses the
* {@link RegisteredBean} to instantiate the underlying bean
* @return a new {@link BeanInstanceSupplier} instance with the specified
* generator
* @return a new {@link BeanInstanceSupplier} instance with the specified generator
*/
public BeanInstanceSupplier<T> withGenerator(
ThrowingFunction<RegisteredBean, T> generator) {
public BeanInstanceSupplier<T> withGenerator(ThrowingFunction<RegisteredBean, T> generator) {
Assert.notNull(generator, "'generator' must not be null");
return new BeanInstanceSupplier<>(this.lookup,
(registeredBean, args) -> generator.apply(registeredBean), this.shortcuts);
@@ -176,10 +174,8 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
/**
* Return a new {@link BeanInstanceSupplier} instance that uses the specified
* {@code generator} supplier to instantiate the underlying bean.
* @param generator a {@link ThrowingSupplier} to instantiate the underlying
* bean
* @return a new {@link BeanInstanceSupplier} instance with the specified
* generator
* @param generator a {@link ThrowingSupplier} to instantiate the underlying bean
* @return a new {@link BeanInstanceSupplier} instance with the specified generator
*/
public BeanInstanceSupplier<T> withGenerator(ThrowingSupplier<T> generator) {
Assert.notNull(generator, "'generator' must not be null");
@@ -282,8 +278,7 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
if (executable instanceof Method method) {
return new MethodParameter(method, index);
}
throw new IllegalStateException(
"Unsupported executable " + executable.getClass().getName());
throw new IllegalStateException("Unsupported executable: " + executable.getClass().getName());
}
private ConstructorArgumentValues resolveArgumentValues(
@@ -303,9 +298,7 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
return resolved;
}
private ValueHolder resolveArgumentValue(BeanDefinitionValueResolver resolver,
ValueHolder valueHolder) {
private ValueHolder resolveArgumentValue(BeanDefinitionValueResolver resolver, ValueHolder valueHolder) {
if (valueHolder.isConverted()) {
return valueHolder;
}
@@ -331,8 +324,7 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
}
try {
try {
return beanFactory.resolveDependency(dependencyDescriptor, beanName,
autowiredBeans, typeConverter);
return beanFactory.resolveDependency(dependencyDescriptor, beanName, autowiredBeans, typeConverter);
}
catch (NoSuchBeanDefinitionException ex) {
if (parameterType.isArray()) {
@@ -348,47 +340,45 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
}
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName,
new InjectionPoint(parameter), ex);
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(parameter), ex);
}
}
@SuppressWarnings("unchecked")
private T instantiate(ConfigurableBeanFactory beanFactory, Executable executable,
Object[] arguments) {
try {
if (executable instanceof Constructor<?> constructor) {
return (T) instantiate(constructor, arguments);
private T instantiate(ConfigurableBeanFactory beanFactory, Executable executable, Object[] args) {
if (executable instanceof Constructor<?> constructor) {
try {
return (T) instantiate(constructor, args);
}
if (executable instanceof Method method) {
return (T) instantiate(beanFactory, method, arguments);
catch (Exception ex) {
throw new BeanInstantiationException(constructor, ex.getMessage(), ex);
}
}
catch (Exception ex) {
throw new BeanCreationException(
"Unable to instantiate bean using " + executable, ex);
if (executable instanceof Method method) {
try {
return (T) instantiate(beanFactory, method, args);
}
catch (Exception ex) {
throw new BeanInstantiationException(method, ex.getMessage(), ex);
}
}
throw new IllegalStateException(
"Unsupported executable " + executable.getClass().getName());
throw new IllegalStateException("Unsupported executable " + executable.getClass().getName());
}
private Object instantiate(Constructor<?> constructor, Object[] arguments) throws Exception {
private Object instantiate(Constructor<?> constructor, Object[] args) throws Exception {
Class<?> declaringClass = constructor.getDeclaringClass();
if (ClassUtils.isInnerClass(declaringClass)) {
Object enclosingInstance = createInstance(declaringClass.getEnclosingClass());
arguments = ObjectUtils.addObjectToArray(arguments, enclosingInstance, 0);
args = ObjectUtils.addObjectToArray(args, enclosingInstance, 0);
}
ReflectionUtils.makeAccessible(constructor);
return constructor.newInstance(arguments);
return constructor.newInstance(args);
}
private Object instantiate(ConfigurableBeanFactory beanFactory, Method method,
Object[] arguments) {
ReflectionUtils.makeAccessible(method);
private Object instantiate(ConfigurableBeanFactory beanFactory, Method method, Object[] args) throws Exception {
Object target = getFactoryMethodTarget(beanFactory, method);
return ReflectionUtils.invokeMethod(method, target, arguments);
ReflectionUtils.makeAccessible(method);
return method.invoke(target, args);
}
@Nullable
@@ -416,13 +406,13 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
return Arrays.stream(parameterTypes).map(Class::getName).collect(Collectors.joining(", "));
}
/**
* Performs lookup of the {@link Executable}.
*/
static abstract class ExecutableLookup {
abstract Executable get(RegisteredBean registeredBean);
}
@@ -433,12 +423,10 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
private final Class<?>[] parameterTypes;
ConstructorLookup(Class<?>[] parameterTypes) {
this.parameterTypes = parameterTypes;
}
@Override
public Executable get(RegisteredBean registeredBean) {
Class<?> beanClass = registeredBean.getBeanClass();
@@ -456,10 +444,8 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
@Override
public String toString() {
return "Constructor with parameter types [%s]".formatted(
toCommaSeparatedNames(this.parameterTypes));
return "Constructor with parameter types [%s]".formatted(toCommaSeparatedNames(this.parameterTypes));
}
}
@@ -474,23 +460,19 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
private final Class<?>[] parameterTypes;
FactoryMethodLookup(Class<?> declaringClass, String methodName,
Class<?>[] parameterTypes) {
FactoryMethodLookup(Class<?> declaringClass, String methodName, Class<?>[] parameterTypes) {
this.declaringClass = declaringClass;
this.methodName = methodName;
this.parameterTypes = parameterTypes;
}
@Override
public Executable get(RegisteredBean registeredBean) {
return get();
}
Method get() {
Method method = ReflectionUtils.findMethod(this.declaringClass,
this.methodName, this.parameterTypes);
Method method = ReflectionUtils.findMethod(this.declaringClass, this.methodName, this.parameterTypes);
Assert.notNull(method, () -> "%s cannot be found".formatted(this));
return method;
}
@@ -501,7 +483,6 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
this.methodName, toCommaSeparatedNames(this.parameterTypes),
this.declaringClass);
}
}
}
@@ -0,0 +1,28 @@
/*
* 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.beans.factory.aot;
/**
* Record class holding key information for beans registered in a bean factory.
*
* @param beanName the name of the registered bean
* @param beanClass the type of the registered bean
* @author Brian Clozel
* @since 6.0.8
*/
record BeanRegistrationKey(String beanName, Class<?> beanClass) {
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,8 @@ import org.springframework.aot.generate.GeneratedMethods;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.generate.MethodReference;
import org.springframework.aot.generate.MethodReference.ArgumentCodeGenerator;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.javapoet.ClassName;
import org.springframework.javapoet.CodeBlock;
@@ -38,6 +40,7 @@ import org.springframework.javapoet.MethodSpec;
* @author Phillip Webb
* @author Sebastien Deleuze
* @author Stephane Nicoll
* @author Brian Clozel
* @since 6.0
* @see BeanRegistrationsAotProcessor
*/
@@ -46,9 +49,10 @@ class BeanRegistrationsAotContribution
private static final String BEAN_FACTORY_PARAMETER_NAME = "beanFactory";
private final Map<String, Registration> registrations;
private final Map<BeanRegistrationKey, Registration> registrations;
BeanRegistrationsAotContribution(Map<String, Registration> registrations) {
BeanRegistrationsAotContribution(Map<BeanRegistrationKey, Registration> registrations) {
this.registrations = registrations;
}
@@ -69,26 +73,23 @@ class BeanRegistrationsAotContribution
GeneratedMethod generatedAliasesMethod = codeGenerator.getMethods().add("registerAliases",
this::generateRegisterAliasesMethod);
beanFactoryInitializationCode.addInitializer(generatedAliasesMethod.toMethodReference());
generateRegisterHints(generationContext.getRuntimeHints(), this.registrations);
}
private void generateRegisterBeanDefinitionsMethod(MethodSpec.Builder method,
GenerationContext generationContext,
BeanRegistrationsCode beanRegistrationsCode) {
GenerationContext generationContext, BeanRegistrationsCode beanRegistrationsCode) {
method.addJavadoc("Register the bean definitions.");
method.addModifiers(Modifier.PUBLIC);
method.addParameter(DefaultListableBeanFactory.class,
BEAN_FACTORY_PARAMETER_NAME);
method.addParameter(DefaultListableBeanFactory.class, BEAN_FACTORY_PARAMETER_NAME);
CodeBlock.Builder code = CodeBlock.builder();
this.registrations.forEach((beanName, registration) -> {
this.registrations.forEach((registeredBean, registration) -> {
MethodReference beanDefinitionMethod = registration.methodGenerator
.generateBeanDefinitionMethod(generationContext,
beanRegistrationsCode);
.generateBeanDefinitionMethod(generationContext, beanRegistrationsCode);
CodeBlock methodInvocation = beanDefinitionMethod.toInvokeCodeBlock(
ArgumentCodeGenerator.none(), beanRegistrationsCode.getClassName());
code.addStatement("$L.registerBeanDefinition($S, $L)",
BEAN_FACTORY_PARAMETER_NAME, beanName,
methodInvocation);
BEAN_FACTORY_PARAMETER_NAME, registeredBean.beanName(), methodInvocation);
});
method.addCode(code.build());
}
@@ -96,18 +97,22 @@ class BeanRegistrationsAotContribution
private void generateRegisterAliasesMethod(MethodSpec.Builder method) {
method.addJavadoc("Register the aliases.");
method.addModifiers(Modifier.PUBLIC);
method.addParameter(DefaultListableBeanFactory.class,
BEAN_FACTORY_PARAMETER_NAME);
method.addParameter(DefaultListableBeanFactory.class, BEAN_FACTORY_PARAMETER_NAME);
CodeBlock.Builder code = CodeBlock.builder();
this.registrations.forEach((beanName, registration) -> {
this.registrations.forEach((registeredBean, registration) -> {
for (String alias : registration.aliases) {
code.addStatement("$L.registerAlias($S, $S)",
BEAN_FACTORY_PARAMETER_NAME, beanName, alias);
code.addStatement("$L.registerAlias($S, $S)", BEAN_FACTORY_PARAMETER_NAME,
registeredBean.beanName(), alias);
}
});
method.addCode(code.build());
}
private void generateRegisterHints(RuntimeHints runtimeHints, Map<BeanRegistrationKey, Registration> registrations) {
registrations.keySet().forEach(beanRegistrationKey -> runtimeHints.reflection()
.registerType(beanRegistrationKey.beanClass(), MemberCategory.INTROSPECT_DECLARED_METHODS));
}
/**
* Gather the necessary information to register a particular bean.
* @param methodGenerator the {@link BeanDefinitionMethodGenerator} to use
@@ -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.
@@ -31,6 +31,7 @@ import org.springframework.lang.Nullable;
* @author Phillip Webb
* @author Sebastien Deleuze
* @author Stephane Nicoll
* @author Brian Clozel
* @since 6.0
*/
class BeanRegistrationsAotProcessor implements BeanFactoryInitializationAotProcessor {
@@ -40,15 +41,15 @@ class BeanRegistrationsAotProcessor implements BeanFactoryInitializationAotProce
public BeanRegistrationsAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
BeanDefinitionMethodGeneratorFactory beanDefinitionMethodGeneratorFactory =
new BeanDefinitionMethodGeneratorFactory(beanFactory);
Map<String, Registration> registrations = new LinkedHashMap<>();
Map<BeanRegistrationKey, Registration> registrations = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
RegisteredBean registeredBean = RegisteredBean.of(beanFactory, beanName);
BeanDefinitionMethodGenerator beanDefinitionMethodGenerator =
beanDefinitionMethodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean);
if (beanDefinitionMethodGenerator != null) {
registrations.put(beanName, new Registration(beanDefinitionMethodGenerator,
beanFactory.getAliases(beanName)));
registrations.put(new BeanRegistrationKey(beanName, registeredBean.getBeanClass()),
new Registration(beanDefinitionMethodGenerator, beanFactory.getAliases(beanName)));
}
}
@@ -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.
@@ -130,10 +130,10 @@ public interface AutowireCapableBeanFactory extends BeanFactory {
* {@link BeanPostProcessor BeanPostProcessors}.
* <p>Note: This is intended for creating a fresh instance, populating annotated
* fields and methods as well as applying all standard bean initialization callbacks.
* Constructor resolution is done via {@link #AUTOWIRE_CONSTRUCTOR}, also influenced
* by {@link SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors}.
* It does <i>not</i> imply traditional by-name or by-type autowiring of properties;
* use {@link #createBean(Class, int, boolean)} for those purposes.
* Constructor resolution is based on Kotlin primary / single public / single non-public,
* with a fallback to the default constructor in ambiguous scenarios, also influenced
* by {@link SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors}
* (e.g. for annotation-driven constructor selection).
* @param beanClass the class of the bean to create
* @return the new bean instance
* @throws BeansException if instantiation or wiring failed
@@ -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.
@@ -188,7 +188,7 @@ public class ConstructorArgumentValues {
* rather than matched multiple times.
* @param value the argument value
*/
public void addGenericArgumentValue(Object value) {
public void addGenericArgumentValue(@Nullable Object value) {
this.genericArgumentValues.add(new ValueHolder(value));
}
@@ -53,6 +53,7 @@ import org.springframework.beans.factory.xml.XmlReaderContext;
import org.springframework.core.io.DescriptiveResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@@ -149,8 +150,10 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
private MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(getClass());
@Nullable
private Binding binding;
@Nullable
private GroovyBeanDefinitionWrapper currentBeanDefinition;
@@ -203,6 +206,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
/**
* Return a specified binding for Groovy variables, if any.
*/
@Nullable
public Binding getBinding() {
return this.binding;
}
@@ -16,9 +16,9 @@
package org.springframework.beans.factory.groovy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import groovy.lang.GroovyObjectSupport;
@@ -30,6 +30,8 @@ import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
@@ -51,58 +53,54 @@ class GroovyBeanDefinitionWrapper extends GroovyObjectSupport {
private static final String DESTROY_METHOD = "destroyMethod";
private static final String SINGLETON = "singleton";
private static final List<String> dynamicProperties = new ArrayList<>(8);
static {
dynamicProperties.add(PARENT);
dynamicProperties.add(AUTOWIRE);
dynamicProperties.add(CONSTRUCTOR_ARGS);
dynamicProperties.add(FACTORY_BEAN);
dynamicProperties.add(FACTORY_METHOD);
dynamicProperties.add(INIT_METHOD);
dynamicProperties.add(DESTROY_METHOD);
dynamicProperties.add(SINGLETON);
}
private static final Set<String> dynamicProperties = Set.of(PARENT, AUTOWIRE, CONSTRUCTOR_ARGS,
FACTORY_BEAN, FACTORY_METHOD, INIT_METHOD, DESTROY_METHOD, SINGLETON);
@Nullable
private String beanName;
private Class<?> clazz;
@Nullable
private final Class<?> clazz;
private Collection<?> constructorArgs;
@Nullable
private final Collection<?> constructorArgs;
@Nullable
private AbstractBeanDefinition definition;
@Nullable
private BeanWrapper definitionWrapper;
@Nullable
private String parentName;
public GroovyBeanDefinitionWrapper(String beanName) {
this.beanName = beanName;
GroovyBeanDefinitionWrapper(String beanName) {
this(beanName, null);
}
public GroovyBeanDefinitionWrapper(String beanName, Class<?> clazz) {
this.beanName = beanName;
this.clazz = clazz;
GroovyBeanDefinitionWrapper(@Nullable String beanName, @Nullable Class<?> clazz) {
this(beanName, clazz, null);
}
public GroovyBeanDefinitionWrapper(String beanName, Class<?> clazz, Collection<?> constructorArgs) {
GroovyBeanDefinitionWrapper(@Nullable String beanName, Class<?> clazz, @Nullable Collection<?> constructorArgs) {
this.beanName = beanName;
this.clazz = clazz;
this.constructorArgs = constructorArgs;
}
@Nullable
public String getBeanName() {
return this.beanName;
}
public void setBeanDefinition(AbstractBeanDefinition definition) {
void setBeanDefinition(AbstractBeanDefinition definition) {
this.definition = definition;
}
public AbstractBeanDefinition getBeanDefinition() {
AbstractBeanDefinition getBeanDefinition() {
if (this.definition == null) {
this.definition = createBeanDefinition();
}
@@ -126,19 +124,17 @@ class GroovyBeanDefinitionWrapper extends GroovyObjectSupport {
return bd;
}
public void setBeanDefinitionHolder(BeanDefinitionHolder holder) {
void setBeanDefinitionHolder(BeanDefinitionHolder holder) {
this.definition = (AbstractBeanDefinition) holder.getBeanDefinition();
this.beanName = holder.getBeanName();
}
public BeanDefinitionHolder getBeanDefinitionHolder() {
BeanDefinitionHolder getBeanDefinitionHolder() {
return new BeanDefinitionHolder(getBeanDefinition(), getBeanName());
}
public void setParent(Object obj) {
if (obj == null) {
throw new IllegalArgumentException("Parent bean cannot be set to a null runtime bean reference!");
}
void setParent(Object obj) {
Assert.notNull(obj, "Parent bean cannot be set to a null runtime bean reference.");
if (obj instanceof String name) {
this.parentName = name;
}
@@ -152,7 +148,7 @@ class GroovyBeanDefinitionWrapper extends GroovyObjectSupport {
getBeanDefinition().setAbstract(false);
}
public GroovyBeanDefinitionWrapper addProperty(String propertyName, Object propertyValue) {
GroovyBeanDefinitionWrapper addProperty(String propertyName, Object propertyValue) {
if (propertyValue instanceof GroovyBeanDefinitionWrapper wrapper) {
propertyValue = wrapper.getBeanDefinition();
}
@@ -163,6 +159,7 @@ class GroovyBeanDefinitionWrapper extends GroovyObjectSupport {
@Override
public Object getProperty(String property) {
Assert.state(this.definitionWrapper != null, "BeanDefinition wrapper not initialized");
if (this.definitionWrapper.isReadableProperty(property)) {
return this.definitionWrapper.getPropertyValue(property);
}
@@ -179,6 +176,7 @@ class GroovyBeanDefinitionWrapper extends GroovyObjectSupport {
}
else {
AbstractBeanDefinition bd = getBeanDefinition();
Assert.state(this.definitionWrapper != null, "BeanDefinition wrapper not initialized");
if (AUTOWIRE.equals(property)) {
if ("byName".equals(newValue)) {
bd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);

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