Compare commits

..

203 Commits

Author SHA1 Message Date
Spring Builds c37f685bb7 Release v6.1.2 2023-12-14 10:28:53 +00:00
Juergen Hoeller d4406507d0 Polishing 2023-12-14 00:12:22 +01:00
Juergen Hoeller a612518f96 Check startup/shutdown thread state for close bypass in shutdown hook
See gh-31811
2023-12-14 00:12:12 +01:00
rstoyanchev ec0ec7a0d6 Avoid nested constructor binding if there are no request parameters
Closes gh-31821
2023-12-13 18:06:16 +00:00
Brian Clozel 0970b1dc7a Optimize ContentCachingRequestWrapper allocation
This commit builds on top of changes made in gh-29775 and gh-31737.
Before this change, we would allocate several byte arrays even in cases
of known request size. This could decrease performance when getting the
cached content as it requires merging several arrays and data is not
colocated in memory.

This change ensures that we create a `FastByteArrayOutputStream`
instance with the known request size so that the first allocated segment
can contain the entire content.
If the request size is not know, we will default back on the default
allocation size for the `FastByteArrayOutputStream`.

Closes gh-31834
2023-12-13 18:47:32 +01:00
Sébastien Deleuze a01c6d57bb Inherit parent context in coRouter DSL
This commit also allows context override, as it
is useful for the nested router use case.

Closes gh-31831
2023-12-13 16:00:22 +01:00
Sam Brannen 8d4deca2a6 Introduce test for XML replaced-method element without explicit arg-type
This commit introduces an integration test for the regression fixed in
the previous commit (cd64e6676c).

Closes gh-31826
2023-12-13 15:04:15 +01:00
Juergen Hoeller cd64e6676c Prepare method overrides when bean class gets resolved
See gh-31826
2023-12-13 14:33:13 +01:00
rstoyanchev 2acc7c609f Refine RequestMappingInfo path initialization
Refining the change from 43700302c6 so that
we consistently pick a PathPatternParser (a) if it is provided, and (b)
if both PathPatternParser and PathMatcher are not provided. Also applying
the same in the mutate builder.

See gh-31662
2023-12-13 12:10:53 +00:00
Stéphane Nicoll 409cecfff9 Add custom code generation for bean definition property values
This commits allows ValueCodeGenerator$Delegate implementations to be
loaded from `META-INF/spring/aot.factories` and considered for code
generation of bean definition property values. This default behavior
in DefaultBeanRegistrationCodeFragments can be customized as usual.

Closes gh-31427
2023-12-13 07:14:55 +01:00
Stéphane Nicoll 3c2c9ca186 Extract value code generation to make it reusable
This commit introduces ValueCodeGenerator and its Delegate interface
as a way to generate the code for a particular value. Implementations
in spring-core provides support for common value types such a String,
primitives, Collections, etc.

Additional implementations are provided for code generation of bean
definition property values.

Closes gh-28999
2023-12-13 07:05:58 +01:00
Sam Brannen 75da9c3c47 Scan annotations on method in interface hierarchy only once
Prior to this commit, the AnnotationsScanner used in the
MergedAnnotations infrastructure found duplicate annotations on methods
within multi-level interface hierarchies.

This commit addresses this issue by scanning methods at a given level
in the interface hierarchy using ReflectionUtils#getDeclaredMethods
instead of Class#getMethods, since the latter includes public methods
declared in super-interfaces which will anyway be scanned when
processing super-interfaces recursively.

Closes gh-31803
2023-12-12 18:28:53 +01:00
Sam Brannen 952223dcf9 Polish MergedAnnotation tests 2023-12-12 17:42:58 +01:00
rstoyanchev 125e2902be Polishing contribution
Closes gh-31778
2023-12-12 16:01:23 +00:00
HeartPattern 4d838c1092 Exclude Part from nested constructor binding in WebFlux
See gh-31778
2023-12-12 16:01:23 +00:00
Stéphane Nicoll c75c0ae2d5 Make sure interface method has hints for init/destroy invocation
This commit matches the behavior of the core container when it
invokes a custom init or destroy method. If the custom method is an
interface implementation, the core container attempts to invoke the
method that's defined on the interface. This commit makes sure to also
register a hint for the interface method.

Closes gh-31819
2023-12-12 14:57:35 +01:00
Sam Brannen c0683cd30b Update copyright headers 2023-12-12 14:51:03 +01:00
Sam Brannen 7471fd1dd0 Remove obsolete code 2023-12-12 14:51:03 +01:00
Sam Brannen 1c58511cb2 Polishing 2023-12-12 14:51:03 +01:00
Brian Clozel 8de0fadd09 Upgrade CI to JDK 22 early access 27 2023-12-12 14:47:50 +01:00
Brian Clozel da01e0c6e2 Upgrade CI to Ubuntu Jammy 20231128 2023-12-12 14:47:24 +01:00
Brian Clozel b87852612b Improve "active" metrics handling in WebClient observations
Prior to this commit, the WebClient observations would have a specific
lifecycle where the observation context is build with a
`ClientRequest.Builder` as tracing needs to add an outgoing request
header before the request is made immutable.

With this setup, the metrics observation handler processes the start
event by increasing the "http.client.requests.active" counter and
collecting tags at this point. Because then the immutable request is not
yet fully built or set on the context, the keyvalues collected by the
observation convention at that point can be incomplete.

This commit ensures that a request is always made available in the
context, even if it is updated right after the observation start. The
only difference between the two should be additional tracing headers and
a request attribute holding the current observation context.

Closes gh-31702
2023-12-12 14:41:41 +01:00
Juergen Hoeller 7adc2f0779 Upgrade to Tomcat 10.1.16 and Jetty 12.0.4 2023-12-12 14:26:56 +01:00
Stéphane Nicoll 33b28fe5bf Upgrade to Reactor 2023.0.1
Closes gh-31814
2023-12-12 13:58:24 +01:00
Stéphane Nicoll b85fcb6aec Polish 2023-12-12 13:56:56 +01:00
Arjen Poutsma 134bb6e31f Document exception wrapping in RestClient status handlers
This commit documents the fact that any (Unchecked)IOExceptions or
HttpMessageNotReadableExceptions thrown from the error handler will be
wrapped in a RestClientException.

Closes gh-31783
2023-12-12 13:34:16 +01:00
Juergen Hoeller 240a75f313 Polishing 2023-12-12 12:40:04 +01:00
Juergen Hoeller 6dcba4de2c Avoid double proxying for @Resource @Lazy fallback autowiring
Includes refactored @Resource resolver for AOT with lazy resolution support.

Closes gh-31447
See gh-29614
2023-12-12 12:39:59 +01:00
Juergen Hoeller 6bb9775309 Declare isStatic and releaseTarget as default methods on TargetSource
Closes gh-31820
2023-12-12 12:39:52 +01:00
Juergen Hoeller eae53560e4 Use ReentrantLock to skip intermediate close attempt from shutdown hook
See gh-31811
2023-12-12 12:39:45 +01:00
Brian Clozel f0abdf2264 Upgrade to Mockito 5.8.0
See gh-31459
2023-12-12 11:22:41 +01:00
Brian Clozel c7f24efc27 Upgrade to Groovy 4.0.16
See gh-31459
2023-12-12 11:22:07 +01:00
Brian Clozel 07d2571e0b Avoid race conditions while restructuring ConcurrentReferenceHashMap
Prior to this commit, the `ConcurrentReferenceHashMap#restructure`
operation would null out the entire references array before starting the
restructuring operation (in case resizing is not necessary).

This could cause at runtime race conditions where a lookup operation
would return null, when the value is actually cached but not accesible
during the restructuring phase.

This commit ensures that, when resizing is not required, a new reference
list is built (purged of null entries) and then assigned to the
reference array. This way, concurrent reads will not return null for
existing entries and there are less chances of re-calculating cache
entries during the restructuring phase.

Closes gh-31008
2023-12-12 10:18:58 +01:00
Juergen Hoeller 8c51315cd6 Avoid potential refresh deadlock with registerShutdownHook
Closes gh-31811
2023-12-11 21:35:58 +01:00
Sébastien Deleuze e8cd26bbf0 Update the badge to 6.1.x in README.md 2023-12-11 21:32:36 +01:00
Sébastien Deleuze 9aded3fcad Fix a NPE in proxied suspending functions
Closes gh-31809
2023-12-11 21:21:34 +01:00
Sébastien Deleuze aabe4d0b07 Pickup CoroutineContext saved by CoWebFilter in coRouter
Closes gh-31793
2023-12-11 19:03:35 +01:00
rstoyanchev 570074259d Minor updates in HandlerMappingIntrospector
Required by Spring Security to complete work on
https://github.com/spring-projects/spring-security/issues/14128

The setCache and resetCache methods used from createCacheFilter are now
public. Generally they don't need to be used outside of the Filter if
only making checks against the current request. Spring Security, however,
makes additional checks against requests with alternative paths.
2023-12-11 15:38:34 +00:00
Stéphane Nicoll f962211e0a Start building against Reactor 2023.0.1 snapshots
See gh-31814
2023-12-11 15:00:19 +01:00
Stéphane Nicoll 6077c998d1 Upgrade to Micrometer 1.12.1
Closes gh-31813
2023-12-11 14:55:55 +01:00
Arjen Poutsma 57b8100a06 Copy headers map in RestClientResponseException to ensure serializability
This commit ensures that the HttpHeaders used are serializable by making
 a copy.

Closes gh-31787
2023-12-11 14:02:31 +01:00
Sam Brannen 7432a96b48 Polish contribution
See gh-31808
2023-12-11 13:42:03 +01:00
Mathieu AMBLARD (u118971) a01384068a Fix Comparators.nullsLow and Comporators.nullsHigh behavior
Commit 33454a4007 introduced a regression in Comparators.nullsLow() and
Comporators.nullsHigh().

This commit updates the code so that nullsLow() sorts null values lower
than non-null values and nullsHigh sorts null values higher than
non-null values.

See gh-25478
Closes gh-31808
2023-12-11 13:18:24 +01:00
Sébastien Deleuze d75a7c3818 Support multiple CoWebFilter changing the context
This commit ensures CoWebFilter merges the exchange
CoroutineContext with the filter one if needed.

Closes gh-31792
2023-12-11 11:35:19 +01:00
Stéphane Nicoll e2c2268c39 Add AOT support for @Resource
This commit adds ahead of time support for @Resource on fields and
methods. Lookup elements are discovered and code is generated to replace
that introspection at runtime.

Closes gh-29614
2023-12-11 11:04:13 +01:00
Juergen Hoeller b510bc3bab Reuse generated key for get+put within same cacheable operation
Closes gh-31789
2023-12-10 20:14:49 +01:00
Sam Brannen b6364e3665 Polish Javadoc 2023-12-10 18:25:01 +01:00
Juergen Hoeller f29bfd9769 Polishing 2023-12-10 00:28:51 +01:00
Juergen Hoeller 361dfd1ae4 Polishing 2023-12-09 23:51:18 +01:00
Juergen Hoeller 8704ad98a7 Clear caches which are not needed after singleton instantiation
Closes gh-28293
2023-12-09 23:50:54 +01:00
Juergen Hoeller c57b7e8418 Introduce ClassFormatException and spring.classformat.ignore property
Closes gh-27691
2023-12-09 20:03:57 +01:00
Juergen Hoeller 77b0382a6c Bypass getParameterType by default for PostgreSQL and SQL Server drivers
Closes gh-25679
2023-12-09 20:03:51 +01:00
Stéphane Nicoll a7f9da1670 Merge pull request #31801 from JuHyun419
* pr/31801:
  Remove unused code

Closes gh-31801
2023-12-09 16:42:17 +01:00
juhyun b782747472 Remove unused code
See gh-31801
2023-12-09 16:40:23 +01:00
Juergen Hoeller 69bc4e2828 Delegation support for JDBC 4.3 ConnectionBuilder and ShardingKeyBuilder
Also moves ShardingKeyProvider to datasource package and declares getSuperShardingKey as default method.

Closes gh-31795
See gh-31506
2023-12-08 23:52:22 +01:00
Mohammed Bekraoui e4e2224449 Support direct shard database operation routing in Spring JDBC (#31506)
Introduce ShardingKeyDataSourceAdapter to get shard connections.

This commit introduces a DataSource proxy, that changes the behavior of the getConnection method to use the `createConnectionBuilder()` api to acquire direct shard connections. The shard connection is acquired by specifying a `ShardingKey` that is correspondent to the wanted shard.
2023-12-08 23:09:39 +01:00
Gabriel Ramirez d919930d83 Fix link text in WebFlux @⁠HttpExchange section of reference docs
Closes gh-31796
2023-12-08 18:31:17 +01:00
Juergen Hoeller afc1f37616 Support for SQL Server named parameter binding (aligned with Sybase)
Closes gh-26072
See gh-30231
2023-12-08 16:38:43 +01:00
Giuseppe Milicia 748dd94dab Fix Sybase SimpleJdbcCall named parameter binding 2023-12-08 16:15:51 +01:00
Henning Poettker b3a3b79b44 Adds MySQLIdentityColumnMaxValueIncrementer
The new `DataFieldMaxValueIncrementer` can be used with identity columns in MySQL 8.0 or later.
2023-12-08 16:12:29 +01:00
Sébastien Deleuze 91b9a75371 Box Kotlin value class parameters in web endpoint
In order to avoid "java.lang.IllegalArgumentException:
object is not an instance of declaring class" errors.

Closes gh-31698
2023-12-08 15:27:08 +01:00
Stéphane Nicoll b78aed99ea Provide a dedicated exception if ScheduledExecutor is not configured
While it is not easily possible to create a ConcurrentTaskScheduler
with no scheduled executor, DefaultManagedTaskScheduler has a default
constructor that does that as the JNDI lookup happens in the regular
afterPropertiesSet callback. If such an instance is created manually,
it can throw an unhelpful NullPointerException if one attempts to
schedule a task.

This commit updates ConcurrentTaskScheduler to flag that the scheduled
executor could be `null` and check if that's the case before using it.
This now throws a dedicated exception that should hopefully provide
better context for those upgrading.

See gh-27914

Closes gh-31751
2023-12-08 15:06:55 +01:00
Stéphane Nicoll 2eba3510f7 Annotate generated classes with @Generated
This commit annotates every generated class with `@Generated` so that
build tools can recognize and ignore those types if necessary.

Closes gh-30824
2023-12-08 14:24:53 +01:00
Stéphane Nicoll 56afd38148 Delete .mailmap
This file is incomplete and has outdated content. Given that several
core committers are not even listed, it does not seem to serve a
valid purpose anymore.

Closes gh-31740
2023-12-08 14:15:32 +01:00
Stéphane Nicoll 33e4129155 Do not discover annotation processors from the classpath in tests
Previously, if an annotation processors was present in the classpath
it was executed as part of tests using `TestCompiler`.

This commit updates `TestCompiler` to always set the annotation
processors to use. By default, this sets an empty list which does not
use annotation processing.

Closes gh-31791
2023-12-08 14:11:38 +01:00
Stéphane Nicoll 0717ea5ca5 Polish 2023-12-08 12:17:44 +01:00
Juergen Hoeller 3b4c7a8906 Revise LazyConnectionDataSourceProxy for late connection properties check
Includes special support for a read-only DataSource in addition to the regular target DataSource, avoiding the overhead of switching the Connection's read-only flag at the beginning and end of every transaction.

Closes gh-29931
Closes gh-31785
Closes gh-19688
Closes gh-21415
2023-12-07 23:14:17 +01:00
Rossen Stoyanchev 2e07f9ab33 DefaultWebClient exposes full URI template as request attribute
Closes gh-30027
2023-12-07 12:16:22 +00:00
rstoyanchev dd23b1d156 Add mappedHandler Predicate to AbstractHandlerExceptionResolver
Closes gh-26772
2023-12-07 12:16:22 +00:00
rstoyanchev 753409083d Add urlDecode property to ServletCookieValueMethodArgumentResolver
Closes gh-26989
2023-12-07 12:16:22 +00:00
Stéphane Nicoll e36d035f58 Remove leftovers
See gh-31690
2023-12-07 10:59:01 +01:00
Sam Brannen 302cdeeee6 Clean up warnings in JdbcTransactionManagerTests 2023-12-06 21:35:02 +01:00
Brian Clozel 61dd9fce73 Revert ValidationMode.CALLBACK on JPA bootstrap
In gh-30549 we applied changes to the `DefaultPersistenceUnitManager` to
use the `ValidationMode.CALLBACK` to better detect invalid validation
setup and enforce bootstrap failures in those cases.

Unfortunately, doing so disables the detection of validation annotation
on entities during the schema creation phase. This is a known Hibernate
issue. This commit reverts this change until HHH-12287 is fixed.

Fixes gh-31726
2023-12-06 18:34:42 +01:00
Sam Brannen 7a221eb581 Update Javadoc for @⁠ComponentScan regarding local overrides
See gh-31704
2023-12-06 18:00:37 +01:00
Sam Brannen 2e5d1daeff Polishing 2023-12-06 17:59:40 +01:00
Sam Brannen bf6cb7cd89 Delete manually crafted section summary for Java-based Container Config
Closes gh-31777
2023-12-06 17:40:12 +01:00
Sam Brannen 2c053b34f0 Delete manually crafted section summary for the SpEL Language Reference
Closes gh-31776
2023-12-06 17:27:35 +01:00
Sam Brannen 438c3818cc Replace System.getProperties().remove(x) with System.clearProperty(x)
This commit migrates to the not-so-new System.clearProperty() method
introduced in Java 1.5.
2023-12-06 17:11:46 +01:00
Sam Brannen f14b122c9c Fix #this and #root variable examples in SpEL documentation
This commit actually introduces a new example for #root variable usage,
which was previously missing.

Closes gh-31770
2023-12-06 16:54:03 +01:00
Sam Brannen 9f305bfaab Polish SpEL examples and tests 2023-12-06 16:54:03 +01:00
Sébastien Deleuze d410872e4f Polish CookieIntegrationTests 2023-12-06 15:01:09 +01:00
Sébastien Deleuze 8fe2c780df Support cookies with the same name with Reactor Netty
Ignore the related test with Undertow due to a bug in
their cookies handling.

Closes gh-28490
2023-12-06 15:01:09 +01:00
Stéphane Nicoll b53ffa3855 Merge pull request #31700 from quaff
* pr/31700:
  Polish contribution
  Add support for location patterns in ResourceArrayPropertyEditor

Closes gh-31700
2023-12-06 15:00:30 +01:00
Stéphane Nicoll 25537938d6 Polish contribution
See gh-31700
2023-12-06 15:00:14 +01:00
Yanming Zhou 9704b809b1 Add support for location patterns in ResourceArrayPropertyEditor
This commit adds support for comma delimited location patterns in
ResourceArrayPropertyEditor.

See gh-31700
2023-12-06 15:00:14 +01:00
Arjen Poutsma 0e6c17f518 Process tokens after each feed in Jackson2Tokenizer
This commit ensures that we process after each fed buffer in
Jackson2Tokenizer, instead of after all fed buffers.

Closes gh-31747
2023-12-06 14:31:05 +01:00
Sam Brannen 1afea0b144 Fix and polish Javadoc for MimeTypeUtils 2023-12-06 14:30:16 +01:00
Johnny Lim 7b95bd72f7 Fix condition for "Too many elements" in MimeTypeUtils.sortBySpecificity()
See gh-31254
Closes gh-31769
2023-12-06 14:09:53 +01:00
Sam Brannen fdcea58a53 Polishing 2023-12-06 12:36:04 +01:00
Arjen Poutsma ef4ffa0005 Support empty part in DefaultPartHttpMessageReader
This commit fixes a bug in DefaultPartHttpMessageReader's
MultipartParser, due to which the last token in a part window was not
properly indicated.

Closes gh-30953
2023-12-06 12:21:37 +01:00
Sébastien Deleuze 2e3d13331a Document @ModelAttribute usage with native images
Closes gh-31765
2023-12-06 12:13:33 +01:00
Sam Brannen 448e753184 Honor classValuesAsString in getMergedRepeatableAnnotationAttributes()
Closes gh-31768
2023-12-06 12:09:39 +01:00
Sam Brannen 6b53f37030 Favor local @⁠ComponentScan annotations over meta-annotations
Work performed in conjunction with gh-30941 resulted in a regression.
Specifically, prior to Spring Framework 6.1 a locally declared
@⁠ComponentScan annotation took precedence over @⁠ComponentScan
meta-annotations, which allowed "local" configuration to override
"meta-present" configuration.

This commit modifies the @⁠ComponentScan search algorithm so that
locally declared @⁠ComponentScan annotations are once again favored
over @⁠ComponentScan meta-annotations (and, indirectly, composed
annotations).

See gh-30941 Closes gh-31704
2023-12-06 11:40:25 +01:00
Yanming Zhou afcd03bddc Replace assertThat(x.isEmpty()).isTrue() with assertThat(x).isEmpty()
Search for   : assertThat\((.+).isEmpty\(\)\).isTrue\(\)
Replace with : assertThat($1).isEmpty()

Search for   : assertThat\((.+).isEmpty\(\)\).isFalse\(\)
Replace with : assertThat($1).isNotEmpty()

Closes gh-31758
2023-12-06 10:04:56 +01:00
Yanming Zhou 7b16ef90f1 Replace assertThat(x.equals(y)) with assertThat(x).isEqualTo(y)
Search for   : assertThat\((.+)\.equals\((\w+)\)\)\.isTrue\(\)
Replace with : assertThat($1).isEqualTo($2)

Search for   : assertThat\((.+)\.equals\((\w+)\)\)\.isFalse\(\)
Replace with : assertThat($1).isNotEqualTo($2)

Closes gh-31763
2023-12-06 09:50:15 +01:00
Yanming Zhou e2852e7355 Replace assertThat(x.contains(y)).isTrue() with assertThat(x).contains(y)
Search for   : assertThat\((.+)\.contains\((.+)\)\)\.isTrue\(\)
Replace with : assertThat($1).contains($2)

Search for   : assertThat\((.+)\.contains\((.+)\)\)\.isFalse\(\)
Replace with : assertThat($1).doesNotContain($2)

Closes gh-31762
2023-12-06 09:48:49 +01:00
Yanming Zhou 1a63257b12 Add missing @Test
Closes gh-31761
2023-12-06 09:48:08 +01:00
Yanming Zhou 66e405525b Replace assertThat(x instanceof y).isTrue() with assertThat(x).isInstanceOf(y.class)
Search for   : assertThat\((.+) instanceof (\w+)\)\.isTrue\(\)
Replace with : assertThat($1).isInstanceOf($2.class)

Search for   : assertThat\((.+) instanceof (\w+)\)\.isFalse\(\)
Replace with : assertThat($1).isNotInstanceOf($2.class)

Closes gh-31760
2023-12-06 09:46:44 +01:00
Yanming Zhou 59815cefce Replace assertThat(x.get(i)). with assertThat(x).element(i).
Search for   : assertThat\((.+)\.get\((\d+)\)\)\.
Replace with : assertThat($1).element($2).

Closes gh-31759
2023-12-06 09:43:59 +01:00
Yanming Zhou 785ad399e9 Replace assertThat(x.iterator().next()) with assertThat(x).element(0)
Search for   : assertThat\((.+).iterator\(\).next\(\)\)
Replace with : assertThat($1).element(0)
2023-12-06 10:52:39 +08:00
Sébastien Deleuze 3f9a809c32 Improve @RegisterReflectionForBinding for enums
Closes gh-31570
2023-12-05 16:53:51 +01:00
Sam Brannen c74d60b9fe Polishing 2023-12-05 16:14:13 +01:00
Sam Brannen db48813181 Polish contribution
See gh-31757
2023-12-05 16:14:13 +01:00
HyeongMokJeong a596c0e226 Introduce overloaded MockPart constructor that accepts the Content-Type
Closes gh-31757
2023-12-05 15:55:33 +01:00
Sam Brannen 462ef95904 Fix typo in Javadoc 2023-12-05 13:05:54 +01:00
Arjen Poutsma 52d4b83dba Partially revert RequestPredicates attribute handling
This commit partially reverts 39786e4790
and c5c843696b, as the approach taken did
not take into account request predicates that query request attributes,
including path variables.

Closes gh-31732
2023-12-05 12:30:15 +01:00
Sam Brannen aa347e5fe6 Polish MutablePropertyValuesTests 2023-12-05 11:43:57 +01:00
Sam Brannen ceba4162bb Replace assertThat(!x).isTrue() with assertThat(x).isFalse()
Search for   : assertThat\(!(.+)\).isTrue\(\)

Replace with : assertThat(\1).isFalse()
2023-12-05 11:41:57 +01:00
Stéphane Nicoll 21560bccd3 Merge pull request #31752 from quaff
* pr/31752:
  Polish "Use idiomatic AssertJ map assertions"
  Use idiomatic AssertJ map assertions

Closes gh-31752
2023-12-05 10:41:09 +01:00
Stéphane Nicoll 1da40b84e7 Polish "Use idiomatic AssertJ map assertions"
See gh-31752
2023-12-05 10:39:33 +01:00
Yanming Zhou 6f11716b6f Use idiomatic AssertJ map assertions
See gh-31752
2023-12-05 10:01:38 +01:00
Juergen Hoeller 47fe61ef79 Introduce lazyTransactionalConnections flag on TransactionAwareDataSourceProxy
Includes revision of JDBC transaction tests.

Closes gh-29423
2023-12-04 18:24:30 +01:00
Juergen Hoeller 8a82da43c9 Defensively wrap fixed-delay task on scheduler thread
Closes gh-31749
2023-12-04 18:20:44 +01:00
Sam Brannen cb60f74556 Stop referring to JDO PersistenceManager in comments 2023-12-04 16:50:53 +01:00
Sam Brannen 62b3d7a963 Update copyright headers 2023-12-04 16:47:25 +01:00
Sam Brannen b69e5acfe3 Revert use of yield in switch expressions due to Eclipse compiler error
See gh-31531
2023-12-04 16:47:25 +01:00
Sam Brannen d71853f105 Polish contribution
See gh-31531
2023-12-04 16:47:25 +01:00
Yanming Zhou 490b5c77fc Use switch expression where feasible 2023-12-04 15:42:55 +01:00
Sam Brannen 8ed04b5dd1 Polish contribution
See gh-31723
2023-12-04 15:17:52 +01:00
Per Lundberg 87d37a21aa Link to spring.factories used in the TestContext framework in the ref docs
Closes gh-31723
2023-12-04 15:04:12 +01:00
Sam Brannen eee2569bff Polishing 2023-12-04 15:04:12 +01:00
Stéphane Nicoll dbec3f1fa1 Polish 2023-12-04 14:27:29 +01:00
Sam Brannen e870912fa2 Polish Javadoc for MockRestServiceServer
See gh-31741
2023-12-03 19:24:46 +01:00
Donghun Shin 99f50ebeb4 Fix Javadoc for MockRestServiceServer.bindTo(RestClient.Builder)
Closes gh-31741
2023-12-03 19:15:35 +01:00
Sam Brannen cd62dfe3a9 Polish FastByteArrayOutputStream[Tests]
See gh-31737
2023-12-02 16:31:28 +01:00
Patrick Strawderman 7cdacf3083 Introduce toString(Charset) in FastByteArrayOutputStream
This commit introduces a toString() overload in
FastByteArrayOutputStream that accepts a Charset in order to mirror the
method that was introduced in ByteArrayOutputStream in JDK 10,
including a special case for when a single buffer is in use internally
to avoid the need to resize.

This commit also updates getContentAsString() in
ContentCachingRequestWrapper to use this new toString(Charset) method.

Closes gh-31737
2023-12-02 16:31:28 +01:00
Stéphane Nicoll 0bec8125a4 Merge pull request #31736 from tylerbertrand
* pr/31736:
  Use relative path for javadoc overview

Closes gh-31736
2023-12-02 16:22:19 +01:00
Tyler Bertrand da32e3d39e Use relative path for javadoc overview
See gh-31736
2023-12-02 16:19:17 +01:00
Sam Brannen 47cdc7c5f0 Update copyright headers
See gh-31738
2023-12-02 15:32:46 +01:00
dogglezz decb22a93d Polish Javadoc
Closes gh-31738
2023-12-02 15:31:39 +01:00
rstoyanchev d59b2924d3 Exclude any Java library type from nested constructor binding
Closes gh-31709
2023-12-01 17:04:49 +00:00
Sam Brannen c05b4ce776 Suppress warnings in Gradle build 2023-12-01 15:44:37 +01:00
Sam Brannen 3a53446a2b Upgrade to Gradle 8.5
Closes gh-31734
2023-12-01 15:37:55 +01:00
Arjen Poutsma d204dd2dbe Use IntrospectingClientHttpResponse in RestClient
This commit ensures that the RestClient uses the
IntrospectingClientHttpResponse to verify whether the response has a
body, and return null if it does not.

See gh-12671
Closes gh-31719
2023-12-01 14:22:58 +01:00
Stéphane Nicoll 0dbb0f5c14 Merge pull request #31731 from kilink
* pr/31731:
  Avoid byte array copy in getContentAsString

Closes gh-31731
2023-12-01 10:58:44 +01:00
Patrick Strawderman e452c2e89c Avoid byte array copy in getContentAsString
The getContentAsString method was originally added in d9b8826 to avoid
the extra copying inherent to calling ByteArrayOutputStream.toByteArray;
however, in f83c609 the class was updated to instead use
FastByteArrayOutputStream, and in the process the extra copy was brought
back when getContentAsString was changed to call toByteArray.

Switch to calling toByteArrayUnsafe, a method provided by
FastByteArrayOutputStream, which avoids the extra copy; since we
immediately pass the byte array to the String constructor, and it isn't
accessed anywhere else, the usage is safe.

See gh-31731
2023-12-01 10:47:38 +01:00
Sam Brannen 6ea9fdbf77 Polishing 2023-11-30 19:04:59 +01:00
Sébastien Deleuze 8ff687b68c Polish BeanDefinitionDsl.kt 2023-11-30 18:17:47 +01:00
Sébastien Deleuze bf1c179b7f Allow to set the order from Kotlin bean DSL
Closes gh-30849
2023-11-30 18:09:00 +01:00
Sébastien Deleuze 16ac495084 Introduce ORDER_ATTRIBUTE on AbstractBeanDefinition
This commit allows to define a bean order programmatically
at bean definition level (functional equivalent of
`@Order`).

If specified, the order attribute defined at bean
definition level overrides potential values set with
`@Order`.

See gh-30849
2023-11-30 18:09:00 +01:00
Sam Brannen a506238ef6 Polishing 2023-11-30 17:59:58 +01:00
Sam Brannen 33af98b6d6 Document need for -parameters flag in cache key generation exception messages
Closes gh-31675
2023-11-30 17:51:54 +01:00
Juergen Hoeller edfe179291 Polishing 2023-11-30 16:36:45 +01:00
Juergen Hoeller 5f9702b2a4 Introduce rollbackBeforeClose property and AutoCloseable implementation
Closes gh-27249
2023-11-30 16:36:32 +01:00
Juergen Hoeller c56c304536 PathEditor considers single-letter URI scheme as NIO path candidate
Closes gh-29881
2023-11-30 14:16:05 +01:00
rstoyanchev 8090a52f5c ForwardedHeaderFilter supports ERROR requestUri attribute
Closes gh-30828
2023-11-30 13:10:17 +00:00
rstoyanchev 19bca03aa2 Polishing in ForwardedHeaderFilter
See gh-30828
2023-11-30 13:10:17 +00:00
rstoyanchev 0e6e225fb9 Implement messageSize methods in JettyWebSocketSession
Closes gh-28325
2023-11-30 13:10:17 +00:00
rstoyanchev 8ca82120e0 Add missing exception name to DisconnectedClientHelper
Closes gh-31717
2023-11-30 13:10:17 +00:00
rstoyanchev 9ade52dbe2 Exclude Part and MultipartFile from nested constructor binding
Closes gh-31669
2023-11-30 13:10:17 +00:00
Yanming Zhou feef98b73c Correct conversion from Resource[] with length 1 to Collection<Resource>
Fix GH-31693
2023-11-30 14:05:13 +01:00
Juergen Hoeller 4a6c3e8f5d Fix reactive retrieval of cached null value for empty Mono
Closes gh-31722
2023-11-30 12:05:16 +01:00
Sébastien Deleuze f77713b7e0 Document testing automatic checkpoint/restore at startup
Closes gh-31724
2023-11-30 11:35:01 +01:00
Stéphane Nicoll dc5bef16b4 Polish Default Profile section
Closes gh-30319
2023-11-29 17:47:27 +01:00
Juergen Hoeller f3b1f37000 Process URL path for filename extraction if URI does not expose path
Closes gh-31718
2023-11-29 17:08:59 +01:00
Sébastien Deleuze df00aafdff Add a nested generics test for GenericTypeResolver
Closes gh-31690
2023-11-29 15:12:07 +01:00
Sébastien Deleuze 7cf124b696 Revert "Support WildcardType resolution in GenericTypeResolver"
This reverts commit f075120675.

See gh-22313
See gh-31690
2023-11-29 15:09:57 +01:00
Brian Clozel 35fcbae8c6 Fix reactive HTTP server Observation instrumentation
Prior to this commit, regressions were introduced with gh-31417:

1. the observation keyvalues would be inconsistent with the HTTP response
2. the observation scope would not cover all controller handlers, causing
  traceIds to be missing

The first issue is caused by the fact that in case of error signals, the
observation was stopped before the response was fully committed, which
means further processing could happen and update the response status.
This commit delays the stop event until the response is committed in
case of errors.

The second problem is caused by the change from a `contextWrite`
operator to using the `tap` operator with a `SignalListener`. The
observation was started in the `doOnSubscription` callback, which is too
late in some cases. If the WebFlux controller handler is synchronous
non-blocking, the execution of the handler is performed before the
subscription happens. This means that for those handlers, the
observation was not started, even if the current observation was
present in the reactor context. This commit changes the
`doOnSubscription` to `doFirst` to ensure that the observation is
started at the right time.

Fixes gh-31703
Fixes gh-31706
2023-11-29 14:39:56 +01:00
Stéphane Nicoll c8e6315a67 Polish 2023-11-29 10:08:20 +01:00
Stéphane Nicoll 61be452402 Quote name attribute if necessary
This commit updates MetadataNamingStrategy to quote an ObjectName
attribute value if necessary. For now, only the name attribute is
handled as it is usually a bean name, and we have no control over
its structure.

Closes gh-23608
2023-11-28 17:05:06 +01:00
Sam Brannen 0cbbd3a0d5 Polishing 2023-11-28 16:21:58 +01:00
Stéphane Nicoll c92a0bd493 Prevent AotDetector to be initialized at build-time
This commit moves the condition used by `@DisabledInAotMode` to a
concrete implementation rather than using `@DisabledIf` as it causes
build initialization in a native image.

Closes gh-31705
2023-11-28 15:46:31 +01:00
Stéphane Nicoll 755fd75512 Merge pull request #31694 from quaff
* pr/31694:
  Upgrade copyright year of changed file
  Use AssertJ isInstanceOf where feasible

Closes gh-31694
2023-11-28 12:58:14 +01:00
Stéphane Nicoll f8a40555af Upgrade copyright year of changed file
See gh-31694
2023-11-28 12:53:54 +01:00
Yanming Zhou 64d5e904e8 Use AssertJ isInstanceOf where feasible
See gh-31694
2023-11-28 12:49:32 +01:00
Stéphane Nicoll 4e2d357318 Remove list of supported websocket servers
This commit removes a list of hardcoded servers and version as this is
bound to get outdated and the reference guide/wiki is a more suitable
place for this.
2023-11-27 16:31:09 +01:00
Stéphane Nicoll 264ec517f2 Polish 2023-11-27 16:31:09 +01:00
Stéphane Nicoll b2e3be10d4 Add test to reproduce behavior
See gh-24502
2023-11-27 16:31:09 +01:00
Sam Brannen 246833329f Register runtime hints for skipped exceptions in the TestContext framework
Closes gh-31479
2023-11-27 15:42:20 +01:00
rstoyanchev 43700302c6 RequestMappingInfo defaults to PathPatternParser
This changes ensures RequestMappingInfo uses PathPatternParser by default
as all AbstractHandlerMapping implementations do as of 6.0.

RequestMappingInfo instances are typically created internally and aligned with
the RequestMappingHandlerMapping in terms of path mapping options.
If a RequestMappingInfo is registered programmatically, the caller needs to also
ensure they are aligned. However, if the two should be aligned by default.

Closes gh-31662
2023-11-27 10:28:14 +00:00
rstoyanchev 54ecbb2bc8 Update link to stompjs library
Closes gh-28409
2023-11-27 10:28:14 +00:00
Stéphane Nicoll 9eb2f29d4a Move sample to unit test
See gh-28904
2023-11-27 07:58:19 +01:00
Sam Brannen 97264c77dd Consistently log exceptions from TestExecutionListeners at WARN level
Prior to this commit, the TestContextManager logged an exception from a
TestExecutionListener at WARN level except in prepareTestInstance()
where such an exception was logged at ERROR level (except for a skipped
exception which is logged at INFO level).

For consistency, this commit modifies TestContextManager so that it
always logs non-skipped exceptions from TestExecutionListeners at WARN
level.

Closes gh-31688
2023-11-26 12:44:13 +01:00
Sam Brannen b17f3bc07b Polishing 2023-11-26 12:31:15 +01:00
Sam Brannen a15f472898 Include scroll() in SharedEntityManagerCreator's queryTerminatingMethods
This commit supports the scroll() and scroll(ScrollMode) methods from
Hibernate's Query API in SharedEntityManagerCreator's query-terminating
methods set.

Closes gh-31682
2023-11-26 12:12:54 +01:00
Stéphane Nicoll 0785256b2f Match HandlerMapping lookup to bean signature
This commit makes sure to initialize any HandlerMapping defined in the
context when searching for resource handlers. Previously, the detection
algorithm was looking up for `SimpleUrlHandlerMapping` while the
declared target type in WebMvcConfigurationSupport is HandlerMapping.
If the application uses lazy initialization, the lookup algorithm would
not force that bean to be initialized.

Closes gh-25488
2023-11-26 09:59:12 +01:00
Sam Brannen 95e250d4bd Log test aborted exceptions at INFO level in the TestContext framework
Prior to this commit, any time an aborted/skipped exception was thrown
by a TestExecutionListener, the TestContextManager unconditionally
logged the exception at WARN level -- or ERROR level for
prepareTestInstance() callbacks.

Regarding the latter, an aborted/skipped exception is certainly not an
ERROR, and in general the associated log output is very verbose
(including a stack trace) and not something the user should be warned
about it.

To improve the user experience, this commit revises TestContextManager
so that it logs such exceptions at INFO level.

Specifically, the following types of exceptions are considered
aborted/skipped exceptions.

- JUnit Jupiter: org.opentest4j.TestAbortedException
- JUnit 4 org.junit.AssumptionViolatedException
- TestNG: org.testng.SkipException

Closes gh-31479
2023-11-25 18:12:43 +01:00
Sam Brannen dbad9fd208 Update copyright headers 2023-11-25 14:59:40 +01:00
Sam Brannen 657b1c6455 Document need for -parameters flag in exception messages
Closes gh-31675
2023-11-25 14:53:56 +01:00
Sam Brannen 23dc5696d9 Polish contribution
See gh-31679
2023-11-25 14:26:45 +01:00
RedStainedInk a56bf87476 Clarify that DI is a type of IoC
The documentation currently states that Inversion of Control (IoC) and
Dependency Injection (DI) are the same thing. Although the two terms
are related, they are not synonymous: DI is a type of IoC.

I believe this change is important because using the terms
interchangeably really muddies the water about the meaning of these two
fundamental concepts of software frameworks.

Closes gh-31679
2023-11-25 14:15:52 +01:00
Stéphane Nicoll c2745de60c Polish 2023-11-25 09:57:13 +01:00
Juergen Hoeller 710373d286 Consider generics in equals method (for ConversionService caching)
Closes gh-31672
2023-11-24 23:25:59 +01:00
Juergen Hoeller 0599320bd8 Filter candidate methods by name first (for more efficient sorting)
Closes gh-28377
2023-11-24 23:25:28 +01:00
Juergen Hoeller 8921be18de Properly return loaded type even if identified as reloadable
Closes gh-31668
2023-11-24 23:25:01 +01:00
Juergen Hoeller 6ff75f157b Detect current schema as indicated by JDBC Connection
Closes gh-28723
2023-11-24 23:24:20 +01:00
Brian Clozel 15d9d9d06a Add current observation context in ClientRequest
Prior to this commit, `ExchangeFilterFunction` could only get the
current observation from the reactor context. This is particularly
useful when such filters want to add KeyValues to the observation
context.

This commit makes this use case easier by adding the context of the
current observation as a request attribute. This also aligns the
behavior with other instrumentations.

Fixes gh-31609
2023-11-24 17:36:15 +01:00
Krane 894dce7cd8 Optimize loops on BeanDefinitionPropertiesCodeGenerator
Closes gh-31650
2023-11-24 16:30:24 +01:00
Stéphane Nicoll 34031ebea9 Escape schema and function name patterns if necessary
The JDBC API that retrieves a proedure or a function allows to specify
patterns for the schema and the procedure name. So far, we've called
this API with the value as is, which does not work if either contains
a wildcard characters that need to be escaped.

This commit updates GenericCallMetadataProvider to escape, if necessary,
the schema or procedure name using the search string escape from the
database metadata.

Closes gh-22725
2023-11-24 15:49:41 +01:00
Brian Clozel b2757d9a21 Add Javadoc to MockHttpServletResponse#getErrorMessage
Closes gh-31386
2023-11-24 14:41:36 +01:00
Sébastien Deleuze 655b4aacb7 Upgrade to Kotlin 1.9.21
Closes gh-31667
2023-11-24 10:43:40 +01:00
Stéphane Nicoll fb4455b396 Polish 2023-11-24 08:38:47 +01:00
Stéphane Nicoll 677b03c36d Merge pull request #31663 from quaff
* pr/31663:
  Polish "Polish GenericTypeResolver Javadoc"
  Polish GenericTypeResolver Javadoc

Closes gh-31663
2023-11-24 08:32:37 +01:00
Stéphane Nicoll 85aa4b65dc Polish "Polish GenericTypeResolver Javadoc"
See gh-31663
2023-11-24 08:30:40 +01:00
Yanming Zhou 0b61755ea3 Polish GenericTypeResolver Javadoc
See gh-31663
2023-11-24 08:28:27 +01:00
Brian Clozel 3bcd30e811 Remove API diff Gradle plugin configuration
We have not published API diffs for a while now so we should remove the
configuration entirely from our build.
2023-11-23 16:19:20 +01:00
Juergen Hoeller 121e3540cc Clear cached executors in setBeanFactory (for reused AspectJ aspect)
Closes gh-28201
2023-11-23 16:15:01 +01:00
Juergen Hoeller 0d8dee2878 Apply fallback for proxy with negative match or annotation pointcut
See gh-30534
2023-11-23 16:14:33 +01:00
Yanming Zhou 3a6d0c1d5b Always fall back to original method if annotation pointcut used
Prior to this commit, AspectJExpressionPointcut doesn't fall back to original method if `!@annotation()` is used, it can cause false positive result.

Fix GH-27119
2023-11-23 16:01:18 +01:00
Stéphane Nicoll 16c6376b3f Merge pull request #31659 from Johannes-Rost
* pr/31659:
  Polish "Polish RestClient Javadoc"
  Polish RestClient Javadoc

Closes gh-31659
2023-11-23 13:01:37 +01:00
Stéphane Nicoll 487dbf8140 Polish "Polish RestClient Javadoc"
See gh-31659
2023-11-23 12:59:03 +01:00
johannesrost e95f8d2922 Polish RestClient Javadoc
See gh-31659
2023-11-23 12:38:16 +01:00
Spring Builds 52b40ffb03 Next development version (v6.1.2-SNAPSHOT) 2023-11-23 08:58:29 +00:00
547 changed files with 10503 additions and 6644 deletions
-36
View File
@@ -1,36 +0,0 @@
Juergen Hoeller <jhoeller@vmware.com>
Juergen Hoeller <jhoeller@vmware.com> <jhoeller@pivotal.io>
Juergen Hoeller <jhoeller@vmware.com> <jhoeller@gopivotal.com>
Rossen Stoyanchev <rstoyanchev@vmware.com>
Rossen Stoyanchev <rstoyanchev@vmware.com> <rstoyanchev@pivotal.io>
Rossen Stoyanchev <rstoyanchev@vmware.com> <rstoyanchev@gopivotal.com>
Phillip Webb <pwebb@vmware.com>
Phillip Webb <pwebb@vmware.com> <pwebb@pivotal.io>
Phillip Webb <pwebb@vmware.com> <pwebb@gopivotal.com>
Chris Beams <cbeams@vmware.com>
Chris Beams <cbeams@vmware.com> <cbeams@pivotal.io>
Chris Beams <cbeams@vmware.com> <cbeams@gopivotal.com>
Arjen Poutsma <poutsmaa@vmware.com>
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@pivotal.io>
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@gopivotal.com>
Arjen Poutsma <poutsmaa@vmware.com> <poutsma@mac.com>
Arjen Poutsma <poutsmaa@vmware.com> <apoutsma@vmware.com>
Oliver Drotbohm <odrotbohm@vmware.com>
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@vmware.com>
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@pivotal.io>
Oliver Drotbohm <odrotbohm@vmware.com> <ogierke@gopivotal.com>
Dave Syer <dsyer@vmware.com>
Dave Syer <dsyer@vmware.com> <dsyer@pivotal.io>
Dave Syer <dsyer@vmware.com> <dsyer@gopivotal.com>
Dave Syer <dsyer@vmware.com> <david_syer@hotmail.com>
Andy Clement <aclement@vmware.com>
Andy Clement <aclement@vmware.com> <aclement@pivotal.io>
Andy Clement <aclement@vmware.com> <aclement@gopivotal.com>
Andy Clement <aclement@vmware.com> <andrew.clement@gmail.com>
Sam Brannen <sbrannen@vmware.com>
Sam Brannen <sbrannen@vmware.com> <sbrannen@pivotal.io>
Sam Brannen <sbrannen@vmware.com> <sam@sambrannen.com>
Simon Basle <sbasle@vmware.com>
Simon Baslé <sbasle@vmware.com>
<dmitry.katsubo@gmail.com> <dmitry.katsubo@gmai.com>
Nick Williams <nicholas@nicholaswilliams.net>
+1 -1
View File
@@ -1,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-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)
# <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.1.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.1.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".
+2 -5
View File
@@ -16,6 +16,8 @@ ext {
javaProjects = subprojects.findAll { !it.name.startsWith("framework-") }
}
description = "Spring Framework"
configure(allprojects) { project ->
apply plugin: "org.springframework.build.localdev"
group = "org.springframework"
@@ -116,8 +118,3 @@ configure([rootProject] + javaProjects) { project ->
configure(moduleProjects) { project ->
apply from: "${rootDir}/gradle/spring-module.gradle"
}
configure(rootProject) {
description = "Spring Framework"
apply plugin: 'org.springframework.build.api-diff'
}
-15
View File
@@ -22,21 +22,6 @@ but doesn't affect the classpath of dependent projects.
This plugin does not provide a `provided` configuration, as the native `compileOnly` and `testCompileOnly`
configurations are preferred.
### API Diff
This plugin uses the [Gradle JApiCmp](https://github.com/melix/japicmp-gradle-plugin) plugin
to generate API Diff reports for each Spring Framework module. This plugin is applied once on the root
project and creates tasks in each framework module. Unlike previous versions of this part of the build,
there is no need for checking out a specific tag. The plugin will fetch the JARs we want to compare the
current working version with. You can generate the reports for all modules or a single module:
```
./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE
./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE
```
The reports are located under `build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/`.
### RuntimeHints Java Agent
+1 -6
View File
@@ -21,18 +21,13 @@ dependencies {
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${javaFormatVersion}"
implementation "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}"
implementation "org.jetbrains.kotlin:kotlin-compiler-embeddable:${kotlinVersion}"
implementation "me.champeau.gradle:japicmp-gradle-plugin:0.3.0"
implementation "org.gradle:test-retry-gradle-plugin:1.4.1"
implementation "org.gradle:test-retry-gradle-plugin:1.5.6"
implementation "io.spring.javaformat:spring-javaformat-gradle-plugin:${javaFormatVersion}"
implementation "io.spring.nohttp:nohttp-gradle:0.0.11"
}
gradlePlugin {
plugins {
apiDiffPlugin {
id = "org.springframework.build.api-diff"
implementationClass = "org.springframework.build.api.ApiDiffPlugin"
}
conventionsPlugin {
id = "org.springframework.build.conventions"
implementationClass = "org.springframework.build.ConventionsPlugin"
@@ -1,142 +0,0 @@
/*
* 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.build.api;
import java.io.File;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import me.champeau.gradle.japicmp.JapicmpPlugin;
import me.champeau.gradle.japicmp.JapicmpTask;
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.jvm.tasks.Jar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link Plugin} that applies the {@code "japicmp-gradle-plugin"}
* and create tasks for all subprojects named {@code "spring-*"}, diffing the public API one by one
* and creating the reports in {@code "build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/"}.
* <p>{@code "./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE"} will output the
* reports for the API diff between the baseline version and the current one for all modules.
* You can limit the report to a single module with
* {@code "./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE"}.
*
* @author Brian Clozel
*/
public class ApiDiffPlugin implements Plugin<Project> {
private static final Logger logger = LoggerFactory.getLogger(ApiDiffPlugin.class);
public static final String TASK_NAME = "apiDiff";
private static final String BASELINE_VERSION_PROPERTY = "baselineVersion";
private static final List<String> PACKAGE_INCLUDES = Collections.singletonList("org.springframework.*");
private static final URI SPRING_MILESTONE_REPOSITORY = URI.create("https://repo.spring.io/milestone");
@Override
public void apply(Project project) {
if (project.hasProperty(BASELINE_VERSION_PROPERTY) && project.equals(project.getRootProject())) {
project.getPluginManager().apply(JapicmpPlugin.class);
project.getPlugins().withType(JapicmpPlugin.class,
plugin -> applyApiDiffConventions(project));
}
}
private void applyApiDiffConventions(Project project) {
String baselineVersion = project.property(BASELINE_VERSION_PROPERTY).toString();
project.subprojects(subProject -> {
if (subProject.getName().startsWith("spring-")) {
createApiDiffTask(baselineVersion, subProject);
}
});
}
private void createApiDiffTask(String baselineVersion, Project project) {
if (isProjectEligible(project)) {
// Add Spring Milestone repository for generating diffs against previous milestones
project.getRootProject()
.getRepositories()
.maven(mavenArtifactRepository -> mavenArtifactRepository.setUrl(SPRING_MILESTONE_REPOSITORY));
JapicmpTask apiDiff = project.getTasks().create(TASK_NAME, JapicmpTask.class);
apiDiff.setDescription("Generates an API diff report with japicmp");
apiDiff.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP);
apiDiff.setOldClasspath(createBaselineConfiguration(baselineVersion, project));
TaskProvider<Jar> jar = project.getTasks().withType(Jar.class).named("jar");
apiDiff.setNewArchives(project.getLayout().files(jar.get().getArchiveFile().get().getAsFile()));
apiDiff.setNewClasspath(getRuntimeClassPath(project));
apiDiff.setPackageIncludes(PACKAGE_INCLUDES);
apiDiff.setOnlyModified(true);
apiDiff.setIgnoreMissingClasses(true);
// Ignore Kotlin metadata annotations since they contain
// illegal HTML characters and fail the report generation
apiDiff.setAnnotationExcludes(Collections.singletonList("@kotlin.Metadata"));
apiDiff.setHtmlOutputFile(getOutputFile(baselineVersion, project));
apiDiff.dependsOn(project.getTasks().getByName("jar"));
}
}
private boolean isProjectEligible(Project project) {
return project.getPlugins().hasPlugin(JavaPlugin.class)
&& project.getPlugins().hasPlugin(MavenPublishPlugin.class);
}
private Configuration createBaselineConfiguration(String baselineVersion, Project project) {
String baseline = String.join(":",
project.getGroup().toString(), project.getName(), baselineVersion);
Dependency baselineDependency = project.getDependencies().create(baseline + "@jar");
Configuration baselineConfiguration = project.getRootProject().getConfigurations().detachedConfiguration(baselineDependency);
try {
// eagerly resolve the baseline configuration to check whether this is a new Spring module
baselineConfiguration.resolve();
return baselineConfiguration;
}
catch (GradleException exception) {
logger.warn("Could not resolve {} - assuming this is a new Spring module.", baseline);
}
return project.getRootProject().getConfigurations().detachedConfiguration();
}
private Configuration getRuntimeClassPath(Project project) {
return project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
}
private File getOutputFile(String baseLineVersion, Project project) {
String buildDirectoryPath = project.getRootProject()
.getLayout().getBuildDirectory().getAsFile().get().getAbsolutePath();
Path outDir = Paths.get(buildDirectoryPath, "reports", "api-diff",
baseLineVersion + "_to_" + project.getRootProject().getVersion());
return project.file(outDir.resolve(project.getName() + ".html").toString());
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
FROM ubuntu:jammy-20231004
FROM ubuntu:jammy-20231128
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
+1 -1
View File
@@ -9,7 +9,7 @@ case "$1" in
echo "https://download.bell-sw.com/java/21.0.1+12/bellsoft-jdk21.0.1+12-linux-amd64.tar.gz"
;;
java22)
echo "https://download.java.net/java/early_access/jdk22/19/GPL/openjdk-22-ea+19_linux-x64_bin.tar.gz"
echo "https://download.java.net/java/early_access/jdk22/27/GPL/openjdk-22-ea+27_linux-x64_bin.tar.gz"
;;
*)
echo $"Unknown java version"
+1 -1
View File
@@ -27,7 +27,7 @@ javadoc {
author = true
header = rootProject.description
use = true
overview = "$rootProject.rootDir/framework-docs/src/docs/api/overview.html"
overview = project.relativePath("$rootProject.rootDir/framework-docs/src/docs/api/overview.html")
destinationDir = file("${project.buildDir}/docs/javadoc-api")
splitIndex = true
links(rootProject.ext.javadocLinks)
@@ -156,6 +156,7 @@ Java::
/**
* Bean definitions for {@link DataSourceConfiguration}
*/
@Generated
public class DataSourceConfiguration__BeanDefinitions {
/**
* Get the bean definition for 'dataSourceConfiguration'
@@ -190,6 +191,9 @@ Java::
NOTE: The exact code generated may differ depending on the exact nature of your bean definitions.
TIP: Each generated class is annotated with `org.springframework.aot.generate.Generated` to
identify them if they need to be excluded, for instance by static analysis tools.
The generated code above creates bean definitions equivalent to the `@Configuration` class, but in a direct way and without the use of reflection if at all possible.
There is a bean definition for `dataSourceConfiguration` and one for `dataSourceBean`.
When a `datasource` instance is required, a `BeanInstanceSupplier` is called.
@@ -516,8 +516,8 @@ as the following example shows:
[[beans-definition-profiles-default]]
=== Default Profile
The default profile represents the profile that is enabled by default. Consider the
following example:
The default profile represents the profile that is enabled if no profile is active. Consider
the following example:
[tabs]
======
@@ -558,9 +558,9 @@ Kotlin::
----
======
If no profile is active, the `dataSource` is created. You can see this
as a way to provide a default definition for one or more beans. If any
profile is enabled, the default profile does not apply.
If xref:#beans-definition-profiles-enable[no profile is active], the `dataSource` is
created. You can see this as a way to provide a default definition for one or more
beans. If any profile is enabled, the default profile does not apply.
The name of the default profile is `default`. You can change the name of
the default profile by using `setDefaultProfiles()` on the `Environment` or,
@@ -1,15 +1,15 @@
[[beans-introduction]]
= Introduction to the Spring IoC Container and Beans
This chapter covers the Spring Framework implementation of the Inversion of Control
(IoC) principle. IoC is also known as dependency injection (DI). It is a process whereby
objects define their dependencies (that is, the other objects they work with) only through
constructor arguments, arguments to a factory method, or properties that are set on the
object instance after it is constructed or returned from a factory method. The container
This chapter covers the Spring Framework implementation of the Inversion of Control (IoC)
principle. Dependency injection (DI) is a specialized form of IoC, whereby objects define
their dependencies (that is, the other objects they work with) only through constructor
arguments, arguments to a factory method, or properties that are set on the object
instance after it is constructed or returned from a factory method. The IoC container
then injects those dependencies when it creates the bean. This process is fundamentally
the inverse (hence the name, Inversion of Control) of the bean itself
controlling the instantiation or location of its dependencies by using direct
construction of classes or a mechanism such as the Service Locator pattern.
the inverse (hence the name, Inversion of Control) of the bean itself controlling the
instantiation or location of its dependencies by using direct construction of classes or
a mechanism such as the Service Locator pattern.
The `org.springframework.beans` and `org.springframework.context` packages are the basis
for Spring Framework's IoC container. The
@@ -3,17 +3,5 @@
:page-section-summary-toc: 1
This section covers how to use annotations in your Java code to configure the Spring
container. It includes the following topics:
* xref:core/beans/java/basic-concepts.adoc[Basic Concepts: `@Bean` and `@Configuration`]
* xref:core/beans/java/instantiating-container.adoc[Instantiating the Spring Container by Using `AnnotationConfigApplicationContext`]
* xref:core/beans/java/bean-annotation.adoc[Using the `@Bean` Annotation]
* xref:core/beans/java/configuration-annotation.adoc[Using the `@Configuration` annotation]
* xref:core/beans/java/composing-configuration-classes.adoc[Composing Java-based Configurations]
* xref:core/beans/environment.adoc#beans-definition-profiles[Bean Definition Profiles]
* xref:core/beans/environment.adoc#beans-property-source-abstraction[`PropertySource` Abstraction]
* xref:core/beans/environment.adoc#beans-using-propertysource[Using `@PropertySource`]
* xref:core/beans/environment.adoc#beans-placeholder-resolution-in-statements[Placeholder Resolution in Statements]
container.
@@ -2,24 +2,4 @@
= Language Reference
:page-section-summary-toc: 1
This section describes how the Spring Expression Language works. It covers the following
topics:
* xref:core/expressions/language-ref/literal.adoc[Literal Expressions]
* xref:core/expressions/language-ref/properties-arrays.adoc[Properties, Arrays, Lists, Maps, and Indexers]
* xref:core/expressions/language-ref/inline-lists.adoc[Inline Lists]
* xref:core/expressions/language-ref/inline-maps.adoc[Inline Maps]
* xref:core/expressions/language-ref/array-construction.adoc[Array Construction]
* xref:core/expressions/language-ref/methods.adoc[Methods]
* xref:core/expressions/language-ref/operators.adoc[Operators]
* xref:core/expressions/language-ref/types.adoc[Types]
* xref:core/expressions/language-ref/constructors.adoc[Constructors]
* xref:core/expressions/language-ref/variables.adoc[Variables]
* xref:core/expressions/language-ref/functions.adoc[User-Defined Functions]
* xref:core/expressions/language-ref/bean-references.adoc[Bean References]
* xref:core/expressions/language-ref/operator-ternary.adoc[Ternary Operator (If-Then-Else)]
* xref:core/expressions/language-ref/operator-elvis.adoc[The Elvis Operator]
* xref:core/expressions/language-ref/operator-safe-navigation.adoc[Safe Navigation Operator]
This section describes how the Spring Expression Language works.
@@ -1,8 +1,8 @@
[[expressions-ref-variables]]
= Variables
You can reference variables in the expression by using the `#variableName` syntax. Variables
are set by using the `setVariable` method on `EvaluationContext` implementations.
You can reference variables in an expression by using the `#variableName` syntax. Variables
are set by using the `setVariable()` method in `EvaluationContext` implementations.
[NOTE]
====
@@ -29,7 +29,7 @@ Java::
context.setVariable("newName", "Mike Tesla");
parser.parseExpression("name = #newName").getValue(context, tesla);
System.out.println(tesla.getName()) // "Mike Tesla"
System.out.println(tesla.getName()); // "Mike Tesla"
----
Kotlin::
@@ -53,8 +53,10 @@ Kotlin::
The `#this` variable is always defined and refers to the current evaluation object
(against which unqualified references are resolved). The `#root` variable is always
defined and refers to the root context object. Although `#this` may vary as components of
an expression are evaluated, `#root` always refers to the root. The following examples
show how to use the `#this` and `#root` variables:
an expression are evaluated, `#root` always refers to the root.
The following example shows how to use the `#this` variable in conjunction with
xref:core/expressions/language-ref/collection-selection.adoc[collection selection].
[tabs]
======
@@ -62,40 +64,95 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// create an array of integers
List<Integer> primes = new ArrayList<>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));
// Create a list of prime integers.
List<Integer> primes = List.of(2, 3, 5, 7, 11, 13, 17);
// create parser and set variable 'primes' as the array of integers
// Create parser and set variable 'primes' as the list of integers.
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataAccess();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("primes", primes);
// all prime numbers > 10 from the list (using selection ?{...})
// evaluates to [11, 13, 17]
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression(
"#primes.?[#this>10]").getValue(context);
// Select all prime numbers > 10 from the list (using selection ?{...}).
String expression = "#primes.?[#this > 10]";
// Evaluates to a list containing [11, 13, 17].
List<Integer> primesGreaterThanTen =
parser.parseExpression(expression).getValue(context, List.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// create an array of integers
val primes = ArrayList<Int>()
primes.addAll(listOf(2, 3, 5, 7, 11, 13, 17))
// Create a list of prime integers.
val primes = listOf(2, 3, 5, 7, 11, 13, 17)
// create parser and set variable 'primes' as the array of integers
// Create parser and set variable 'primes' as the list of integers.
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataAccess()
val context = SimpleEvaluationContext.forReadWriteDataBinding().build()
context.setVariable("primes", primes)
// all prime numbers > 10 from the list (using selection ?{...})
// evaluates to [11, 13, 17]
val primesGreaterThanTen = parser.parseExpression(
"#primes.?[#this>10]").getValue(context) as List<Int>
// Select all prime numbers > 10 from the list (using selection ?{...}).
val expression = "#primes.?[#this > 10]"
// Evaluates to a list containing [11, 13, 17].
val primesGreaterThanTen = parser.parseExpression(expression)
.getValue(context) as List<Int>
----
======
The following example shows how to use the `#this` and `#root` variables together in
conjunction with
xref:core/expressions/language-ref/collection-projection.adoc[collection projection].
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// Create parser and evaluation context.
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
// Create an inventor to use as the root context object.
Inventor tesla = new Inventor("Nikola Tesla");
tesla.setInventions("Telephone repeater", "Tesla coil transformer");
// Iterate over all inventions of the Inventor referenced as the #root
// object, and generate a list of strings whose contents take the form
// "<inventor's name> invented the <invention>." (using projection !{...}).
String expression = "#root.inventions.![#root.name + ' invented the ' + #this + '.']";
// Evaluates to a list containing:
// "Nikola Tesla invented the Telephone repeater."
// "Nikola Tesla invented the Tesla coil transformer."
List<String> results = parser.parseExpression(expression)
.getValue(context, tesla, List.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// Create parser and evaluation context.
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadWriteDataBinding().build()
// Create an inventor to use as the root context object.
val tesla = Inventor("Nikola Tesla")
tesla.setInventions("Telephone repeater", "Tesla coil transformer")
// Iterate over all inventions of the Inventor referenced as the #root
// object, and generate a list of strings whose contents take the form
// "<inventor's name> invented the <invention>." (using projection !{...}).
val expression = "#root.inventions.![#root.name + ' invented the ' + #this + '.']"
// Evaluates to a list containing:
// "Nikola Tesla invented the Telephone repeater."
// "Nikola Tesla invented the Tesla coil transformer."
val results = parser.parseExpression(expression)
.getValue(context, tesla, List::class.java)
----
======
@@ -230,6 +230,19 @@ provided you stick to the required connection lookup pattern. Note that JTA does
savepoints or custom isolation levels and has a different timeout mechanism but otherwise
exposes similar behavior in terms of JDBC resources and JDBC commit/rollback management.
For JTA-style lazy retrieval of actual resource connections, Spring provides a
corresponding `DataSource` proxy class for the target connection pool: see
{spring-framework-api}/jdbc/datasource/LazyConnectionDataSourceProxy.html[`LazyConnectionDataSourceProxy`].
This is particularly useful for potentially empty transactions without actual statement
execution (never fetching an actual resource in such a scenario), and also in front of
a routing `DataSource` which means to take the transaction-synchronized read-only flag
and/or isolation level into account (e.g. `IsolationLevelDataSourceRouter`).
`LazyConnectionDataSourceProxy` also provides special support for a read-only connection
pool to use during a read-only transaction, avoiding the overhead of switching the JDBC
Connection's read-only flag at the beginning and end of every transaction when fetching
it from the primary connection pool (which may be costly depending on the JDBC driver).
NOTE: As of 5.3, Spring provides an extended `JdbcTransactionManager` variant which adds
exception translation capabilities on commit/rollback (aligned with `JdbcTemplate`).
Where `DataSourceTransactionManager` will only ever throw `TransactionSystemException`
@@ -407,6 +407,12 @@ exposes the Hibernate transaction as a JDBC transaction if you have set up the p
`DataSource` for which the transactions are supposed to be exposed through the
`dataSource` property of the `HibernateTransactionManager` class.
For JTA-style lazy retrieval of actual resource connections, Spring provides a
corresponding `DataSource` proxy class for the target connection pool: see
{spring-framework-api}/jdbc/datasource/LazyConnectionDataSourceProxy.html[`LazyConnectionDataSourceProxy`].
This is particularly useful for Hibernate read-only transactions which can often
be processed from a local cache rather than hitting the database.
[[orm-hibernate-resources]]
== Comparing Container-managed and Locally Defined Resources
@@ -506,13 +506,20 @@ if you have not already done so, to get more detailed coverage of Spring's decla
The recommended strategy for JPA is local transactions through JPA's native transaction
support. Spring's `JpaTransactionManager` provides many capabilities known from local
JDBC transactions (such as transaction-specific isolation levels and resource-level
read-only optimizations) against any regular JDBC connection pool (no XA requirement).
read-only optimizations) against any regular JDBC connection pool, without requiring
a JTA transaction coordinator and XA-capable resources.
Spring JPA also lets a configured `JpaTransactionManager` expose a JPA transaction
to JDBC access code that accesses the same `DataSource`, provided that the registered
`JpaDialect` supports retrieval of the underlying JDBC `Connection`.
Spring provides dialects for the EclipseLink and Hibernate JPA implementations.
See the xref:data-access/orm/jpa.adoc#orm-jpa-dialect[next section] for details on the `JpaDialect` mechanism.
`JpaDialect` supports retrieval of the underlying JDBC `Connection`. Spring provides
dialects for the EclipseLink and Hibernate JPA implementations. See the
xref:data-access/orm/jpa.adoc#orm-jpa-dialect[next section] for details on `JpaDialect`.
For JTA-style lazy retrieval of actual resource connections, Spring provides a
corresponding `DataSource` proxy class for the target connection pool: see
{spring-framework-api}/jdbc/datasource/LazyConnectionDataSourceProxy.html[`LazyConnectionDataSourceProxy`].
This is particularly useful for JPA read-only transactions which can often
be processed from a local cache rather than hitting the database.
[[orm-jpa-dialect]]
@@ -15,8 +15,7 @@ Conceptually, checkpoint and restore align with the xref:core/beans/factory-natu
== On-demand checkpoint/restore of a running application
A checkpoint can be created on demand, for example using a command like `jcmd application.jar JDK.checkpoint`. Before the creation of the checkpoint, Spring Framework
stops all the running beans, giving them a chance to close resources if needed by implementing `Lifecycle.stop`. After restore, the same beans are restarted, with `Lifecycle.start` allowing beans to reopen resources when relevant. For libraries that do not depend on Spring, custom checkpoint/restore integration can be provided by implementing `org.crac.Resource` and registering the related instance.
A checkpoint can be created on demand, for example using a command like `jcmd application.jar JDK.checkpoint`. Before the creation of the checkpoint, Spring stops all the running beans, giving them a chance to close resources if needed by implementing `Lifecycle.stop`. After restore, the same beans are restarted, with `Lifecycle.start` allowing beans to reopen resources when relevant. For libraries that do not depend on Spring, custom checkpoint/restore integration can be provided by implementing `org.crac.Resource` and registering the related instance.
WARNING: Leveraging checkpoint/restore of a running application typically requires additional lifecycle management to gracefully stop and start using resources like files or sockets and stop active threads.
@@ -29,6 +28,11 @@ startup during the `LifecycleProcessor.onRefresh` phase. After this phase has co
`InitializingBean#afterPropertiesSet` callbacks have been invoked; but the lifecycle has not started, and the
`ContextRefreshedEvent` has not yet been published.
For testing purposes, it is also possible to leverage the `-Dspring.context.exit=onRefresh` JVM system property which
triggers similar behavior, but instead of creating a checkpoint, it exits your Spring application at the same lifecycle
phase without requiring the Project CraC dependency/JVM or Linux. This can be useful to check if connections to remote
services are required when the beans are not started, and potentially refine the configuration to avoid that.
WARNING: As mentioned above, and especially in use cases where the CRaC files are shipped as part of a deployable artifact (a container image for example), operate with the assumption that any sensitive data "seen" by the JVM ends up in the CRaC files, and assess carefully the related security implications.
NOTE: Automatic checkpoint/restore is a way to "fast-forward" the startup of the application to a phase where the application context is about to start, but it does not allow to have a fully warmed-up JVM.
@@ -19,7 +19,7 @@ been published.
To create the archive, two additional JVM flags must be specified:
* `-XX:ArchiveClassesAtExit=app-cds.jsa`: creates the CDS archive on exit
* `-XX:ArchiveClassesAtExit=application.jsa`: creates the CDS archive on exit
* `-Dspring.context.exit=onRefresh`: starts and then immediately exits your Spring
application as described above
@@ -40,8 +40,8 @@ The base CDS archive can be created by issuing the following command:
== Using the Archive
Once the archive is available, add `-XX:SharedArchiveFile=app-cds.jsa` to your startup
script to use it, assuming a `app-cds.jsa` file in the working directory.
Once the archive is available, add `-XX:SharedArchiveFile=application.jsa` to your startup
script to use it, assuming an `application.jsa` file in the working directory.
To figure out how effective the cache is, you can enable class loading logs by adding
an extra attribute: `-Xlog:class+load:file=cds.log`. This creates a `cds.log` with every
@@ -45,7 +45,7 @@ xref:appendix.adoc#appendix-spring-properties[`SpringProperties`] mechanism.
[NOTE]
====
The `@ContextHierarchy` annotation is currently not supported in AOT mode.
The `@ContextHierarchy` annotation is not supported in AOT mode.
====
To provide test-specific runtime hints for use within a GraalVM native image, you have
@@ -42,13 +42,15 @@ become cumbersome if a custom factory needs to be used across an entire test sui
issue is addressed through support for automatic discovery of default
`ContextCustomizerFactory` implementations through the `SpringFactoriesLoader` mechanism.
Specifically, the modules that make up the testing support in Spring Framework and Spring
For example, the modules that make up the testing support in Spring Framework and Spring
Boot declare all core default `ContextCustomizerFactory` implementations under the
`org.springframework.test.context.ContextCustomizerFactory` key in their
`META-INF/spring.factories` properties files. Third-party frameworks and developers can
contribute their own `ContextCustomizerFactory` implementations to the list of default
factories in the same manner through their own `META-INF/spring.factories` properties
files.
`META-INF/spring.factories` properties files. The `spring.factories` file for the
`spring-test` module can be viewed
{spring-framework-code}/spring-test/src/main/resources/META-INF/spring.factories[here].
Third-party frameworks and developers can contribute their own `ContextCustomizerFactory`
implementations to the list of default factories in the same manner through their own
`spring.factories` files.
[[testcontext-context-customizers-merging]]
@@ -80,12 +80,12 @@ become cumbersome if a custom listener needs to be used across an entire test su
issue is addressed through support for automatic discovery of default
`TestExecutionListener` implementations through the `SpringFactoriesLoader` mechanism.
Specifically, the `spring-test` module declares all core default `TestExecutionListener`
For example, the `spring-test` module declares all core default `TestExecutionListener`
implementations under the `org.springframework.test.context.TestExecutionListener` key in
its `META-INF/spring.factories` properties file. Third-party frameworks and developers
can contribute their own `TestExecutionListener` implementations to the list of default
listeners in the same manner through their own `META-INF/spring.factories` properties
file.
its {spring-framework-code}/spring-test/src/main/resources/META-INF/spring.factories[`META-INF/spring.factories`
properties file]. Third-party frameworks and developers can contribute their own
`TestExecutionListener` implementations to the list of default listeners in the same
manner through their own `spring.factories` files.
[[testcontext-tel-config-ordering]]
== Ordering `TestExecutionListener` Implementations
@@ -207,4 +207,3 @@ Kotlin::
}
----
======
@@ -198,6 +198,9 @@ controller method xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation]
TIP: Using `@ModelAttribute` is optional. By default, any argument that is not a simple
value type as determined by
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]
_AND_ that is not resolved by any other argument resolver is treated as an `@ModelAttribute`.
_AND_ that is not resolved by any other argument resolver is treated as an implicit `@ModelAttribute`.
WARNING: When compiling to a native image with GraalVM, the implicit `@ModelAttribute`
support described above does not allow proper ahead-of-time inference of related data
binding reflection hints. As a consequence, it is recommended to explicitly annotate
method parameters with `@ModelAttribute` for use in a GraalVM native image.
@@ -509,7 +509,7 @@ Kotlin::
[[webflux-ann-httpexchange-annotation]]
== `@HttpExchange`
[.small]#xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-httpexchange-annotation[See equivalent in the Reactive stack]#
[.small]#xref:web/webmvc/mvc-controller/ann-requestmapping.adoc#mvc-ann-httpexchange-annotation[See equivalent in the Servlet stack]#
As an alternative to `@RequestMapping`, you can also handle requests with `@HttpExchange`
methods. Such methods are declared on an
@@ -243,4 +243,9 @@ xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation].
TIP: Using `@ModelAttribute` is optional. By default, any parameter that is not a simple
value type as determined by
{spring-framework-api}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]
_AND_ that is not resolved by any other argument resolver is treated as an `@ModelAttribute`.
_AND_ that is not resolved by any other argument resolver is treated as an implicit `@ModelAttribute`.
WARNING: When compiling to a native image with GraalVM, the implicit `@ModelAttribute`
support described above does not allow proper ahead-of-time inference of related data
binding reflection hints. As a consequence, it is recommended to explicitly annotate
method parameters with `@ModelAttribute` for use in a GraalVM native image.
@@ -103,9 +103,9 @@ You can also use the WebSocket transport configuration shown earlier to configur
maximum allowed size for incoming STOMP messages. In theory, a WebSocket
message can be almost unlimited in size. In practice, WebSocket servers impose
limits -- for example, 8K on Tomcat and 64K on Jetty. For this reason, STOMP clients
(such as the JavaScript https://github.com/JSteunou/webstomp-client[webstomp-client]
and others) split larger STOMP messages at 16K boundaries and send them as multiple
WebSocket messages, which requires the server to buffer and re-assemble.
such as https://github.com/stomp-js/stompjs[`stomp-js/stompjs`] and others split larger
STOMP messages at 16K boundaries and send them as multiple WebSocket messages,
which requires the server to buffer and re-assemble.
Spring's STOMP-over-WebSocket support does this ,so applications can configure the
maximum size for STOMP messages irrespective of WebSocket server-specific message
+9 -9
View File
@@ -8,19 +8,19 @@ javaPlatform {
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.15.2"))
api(platform("io.micrometer:micrometer-bom:1.12.0"))
api(platform("io.micrometer:micrometer-bom:1.12.1"))
api(platform("io.netty:netty-bom:4.1.101.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2023.0.0"))
api(platform("io.projectreactor:reactor-bom:2023.0.1"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.15"))
api(platform("org.apache.groovy:groovy-bom:4.0.16"))
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.3"))
api(platform("org.eclipse.jetty:jetty-bom:12.0.4"))
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.3"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.0"))
api(platform("org.junit:junit-bom:5.10.1"))
api(platform("org.mockito:mockito-bom:5.7.0"))
api(platform("org.mockito:mockito-bom:5.8.0"))
constraints {
api("com.fasterxml:aalto-xml:1.3.2")
@@ -102,10 +102,10 @@ dependencies {
api("org.apache.httpcomponents.client5:httpclient5:5.2.1")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.3")
api("org.apache.poi:poi-ooxml:5.2.4")
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.15")
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.15")
api("org.apache.tomcat:tomcat-util:10.1.15")
api("org.apache.tomcat:tomcat-websocket:10.1.15")
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.16")
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.16")
api("org.apache.tomcat:tomcat-util:10.1.16")
api("org.apache.tomcat:tomcat-websocket:10.1.16")
api("org.aspectj:aspectjrt:1.9.20.1")
api("org.aspectj:aspectjtools:1.9.20.1")
api("org.aspectj:aspectjweaver:1.9.20.1")
+2 -2
View File
@@ -1,10 +1,10 @@
version=6.1.1
version=6.1.2
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
org.gradle.parallel=true
kotlinVersion=1.9.20
kotlinVersion=1.9.21
kotlin.jvm.target.validation.mode=ignore
kotlin.stdlib.default.dependency=false
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-8.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
@@ -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.
@@ -49,10 +49,13 @@ public interface TargetSource extends TargetClassAware {
* Will all calls to {@link #getTarget()} return the same object?
* <p>In that case, there will be no need to invoke {@link #releaseTarget(Object)},
* and the AOP framework can cache the return value of {@link #getTarget()}.
* <p>The default implementation returns {@code false}.
* @return {@code true} if the target is immutable
* @see #getTarget
*/
boolean isStatic();
default boolean isStatic() {
return false;
}
/**
* Return a target instance. Invoked immediately before the
@@ -67,9 +70,11 @@ public interface TargetSource extends TargetClassAware {
/**
* Release the given target object obtained from the
* {@link #getTarget()} method, if any.
* <p>The default implementation is empty.
* @param target object obtained from a call to {@link #getTarget()}
* @throws Exception if the object can't be released
*/
void releaseTarget(Object target) throws Exception;
default void releaseTarget(Object target) throws Exception {
}
}
@@ -78,6 +78,7 @@ import org.springframework.util.StringUtils;
* @author Juergen Hoeller
* @author Ramnivas Laddad
* @author Dave Syer
* @author Yanming Zhou
* @since 2.0
*/
@SuppressWarnings("serial")
@@ -471,10 +472,11 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
}
}
if (targetMethod != originalMethod && (shadowMatch == null ||
(shadowMatch.neverMatches() && Proxy.isProxyClass(targetMethod.getDeclaringClass())))) {
(Proxy.isProxyClass(targetMethod.getDeclaringClass()) &&
(shadowMatch.neverMatches() || containsAnnotationPointcut())))) {
// Fall back to the plain original method in case of no resolvable match or a
// negative match on a proxy class (which doesn't carry any annotations on its
// redeclared methods).
// redeclared methods), as well as for annotation pointcuts.
methodToMatch = originalMethod;
try {
shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);
@@ -513,6 +515,10 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
return shadowMatch;
}
private boolean containsAnnotationPointcut() {
return resolveExpression().contains("@annotation");
}
@Override
public boolean equals(@Nullable Object other) {
@@ -38,8 +38,8 @@ abstract class CoroutinesUtils {
@Nullable
@SuppressWarnings({"unchecked", "rawtypes"})
static Object awaitSingleOrNull(Object value, Object continuation) {
return MonoKt.awaitSingleOrNull(value instanceof Mono mono ? mono : Mono.just(value),
static Object awaitSingleOrNull(@Nullable Object value, Object continuation) {
return MonoKt.awaitSingleOrNull(value instanceof Mono mono ? mono : Mono.justOrEmpty(value),
(Continuation<Object>) continuation);
}
@@ -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.
@@ -73,8 +73,6 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
protected final Log logger = LogFactory.getLog(getClass());
private final Map<Method, AsyncTaskExecutor> executors = new ConcurrentHashMap<>(16);
private SingletonSupplier<Executor> defaultExecutor;
private SingletonSupplier<AsyncUncaughtExceptionHandler> exceptionHandler;
@@ -85,6 +83,9 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
@Nullable
private StringValueResolver embeddedValueResolver;
private final Map<Method, AsyncTaskExecutor> executors = new ConcurrentHashMap<>(16);
/**
* Create a new instance with a default {@link AsyncUncaughtExceptionHandler}.
* @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor}
@@ -157,6 +158,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
if (beanFactory instanceof ConfigurableBeanFactory configurableBeanFactory) {
this.embeddedValueResolver = new EmbeddedValueResolver(configurableBeanFactory);
}
this.executors.clear();
}
@@ -153,16 +153,6 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
}
}
@Override
public boolean isStatic() {
return false;
}
@Override
public void releaseTarget(Object target) throws Exception {
// Nothing to do here.
}
/**
* Copy configuration from the other AbstractBeanFactoryBasedTargetSource object.
@@ -72,11 +72,6 @@ public abstract class AbstractLazyCreationTargetSource implements TargetSource {
return (this.lazyTarget != null ? this.lazyTarget.getClass() : null);
}
@Override
public boolean isStatic() {
return false;
}
/**
* Returns the lazy-initialized target object,
* creating it on-the-fly if it doesn't exist already.
@@ -91,11 +86,6 @@ public abstract class AbstractLazyCreationTargetSource implements TargetSource {
return this.lazyTarget;
}
@Override
public void releaseTarget(Object target) throws Exception {
// nothing to do
}
/**
* Subclasses should implement this method to return the lazy initialized object.
@@ -116,13 +116,6 @@ public final class EmptyTargetSource implements TargetSource, Serializable {
return null;
}
/**
* Nothing to release.
*/
@Override
public void releaseTarget(Object target) {
}
/**
* Returns the canonical instance on deserialization in case
@@ -66,21 +66,11 @@ public class HotSwappableTargetSource implements TargetSource, Serializable {
return this.target.getClass();
}
@Override
public final boolean isStatic() {
return false;
}
@Override
public synchronized Object getTarget() {
return this.target;
}
@Override
public void releaseTarget(Object target) {
// nothing to do
}
/**
* Swap the target, returning the old target object.
@@ -67,11 +67,6 @@ public class SingletonTargetSource implements TargetSource, Serializable {
return this.target;
}
@Override
public void releaseTarget(Object target) {
// nothing to do
}
@Override
public boolean isStatic() {
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.
@@ -73,14 +73,6 @@ public abstract class AbstractRefreshableTargetSource implements TargetSource, R
return this.targetObject.getClass();
}
/**
* Not static.
*/
@Override
public boolean isStatic() {
return false;
}
@Override
@Nullable
public final synchronized Object getTarget() {
@@ -90,13 +82,6 @@ public abstract class AbstractRefreshableTargetSource implements TargetSource, R
return this.targetObject;
}
/**
* No need to release target.
*/
@Override
public void releaseTarget(Object object) {
}
@Override
public final synchronized void refresh() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* @author Rob Harrop
* @author Rod Johnson
* @author Chris Beams
* @author Yanming Zhou
*/
public class AspectJExpressionPointcutTests {
@@ -424,6 +425,19 @@ public class AspectJExpressionPointcutTests {
assertThat(ajexp.matches(BeanA.class.getMethod("getAge"), proxy.getClass())).isTrue();
}
@Test
public void testNotAnnotationOnCglibProxyMethod() throws Exception {
String expression = "!@annotation(test.annotation.transaction.Tx)";
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(expression);
ProxyFactory factory = new ProxyFactory(new BeanA());
factory.setProxyTargetClass(true);
BeanA proxy = (BeanA) factory.getProxy();
assertThat(ajexp.matches(BeanA.class.getMethod("getAge"), proxy.getClass())).isFalse();
}
@Test
public void testAnnotationOnDynamicProxyMethod() throws Exception {
String expression = "@annotation(test.annotation.transaction.Tx)";
@@ -436,6 +450,18 @@ public class AspectJExpressionPointcutTests {
assertThat(ajexp.matches(IBeanA.class.getMethod("getAge"), proxy.getClass())).isTrue();
}
@Test
public void testNotAnnotationOnDynamicProxyMethod() throws Exception {
String expression = "!@annotation(test.annotation.transaction.Tx)";
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
ajexp.setExpression(expression);
ProxyFactory factory = new ProxyFactory(new BeanA());
factory.setProxyTargetClass(false);
IBeanA proxy = (IBeanA) factory.getProxy();
assertThat(ajexp.matches(IBeanA.class.getMethod("getAge"), proxy.getClass())).isFalse();
}
@Test
public void testAnnotationOnMethodWithWildcard() throws Exception {
String expression = "execution(@(test.annotation..*) * *(..))";
@@ -354,8 +354,8 @@ public class ProxyFactoryTests {
list.add(proxy1);
list.add(proxy2);
AnnotationAwareOrderComparator.sort(list);
assertThat(list.get(0)).isSameAs(proxy2);
assertThat(list.get(1)).isSameAs(proxy1);
assertThat(list).element(0).isSameAs(proxy2);
assertThat(list).element(1).isSameAs(proxy1);
}
@Test
@@ -370,8 +370,8 @@ public class ProxyFactoryTests {
list.add(proxy1);
list.add(proxy2);
AnnotationAwareOrderComparator.sort(list);
assertThat(list.get(0)).isSameAs(proxy2);
assertThat(list.get(1)).isSameAs(proxy1);
assertThat(list).element(0).isSameAs(proxy2);
assertThat(list).element(1).isSameAs(proxy1);
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,8 +39,8 @@ public class ScopedProxyAutowireTests {
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
qualifiedResource(ScopedProxyAutowireTests.class, "scopedAutowireFalse.xml"));
assertThat(Arrays.asList(bf.getBeanNamesForType(TestBean.class, false, false)).contains("scoped")).isTrue();
assertThat(Arrays.asList(bf.getBeanNamesForType(TestBean.class, true, false)).contains("scoped")).isTrue();
assertThat(Arrays.asList(bf.getBeanNamesForType(TestBean.class, false, false))).contains("scoped");
assertThat(Arrays.asList(bf.getBeanNamesForType(TestBean.class, true, false))).contains("scoped");
assertThat(bf.containsSingleton("scoped")).isFalse();
TestBean autowired = (TestBean) bf.getBean("autowired");
TestBean unscoped = (TestBean) bf.getBean("unscoped");
@@ -53,8 +53,8 @@ public class ScopedProxyAutowireTests {
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
qualifiedResource(ScopedProxyAutowireTests.class, "scopedAutowireTrue.xml"));
assertThat(Arrays.asList(bf.getBeanNamesForType(TestBean.class, true, false)).contains("scoped")).isTrue();
assertThat(Arrays.asList(bf.getBeanNamesForType(TestBean.class, false, false)).contains("scoped")).isTrue();
assertThat(Arrays.asList(bf.getBeanNamesForType(TestBean.class, true, false))).contains("scoped");
assertThat(Arrays.asList(bf.getBeanNamesForType(TestBean.class, false, false))).contains("scoped");
assertThat(bf.containsSingleton("scoped")).isFalse();
TestBean autowired = (TestBean) bf.getBean("autowired");
TestBean scoped = (TestBean) bf.getBean("scoped");
@@ -141,7 +141,7 @@ public class ComposablePointcutTests {
pc1.intersection(GETTER_METHOD_MATCHER);
assertThat(pc1.equals(pc2)).isFalse();
assertThat(pc1).isNotEqualTo(pc2);
assertThat(pc1.hashCode()).isNotEqualTo(pc2.hashCode());
pc2.intersection(GETTER_METHOD_MATCHER);
@@ -110,8 +110,8 @@ public class MethodMatchersTests {
public void testUnionEquals() {
MethodMatcher first = MethodMatchers.union(MethodMatcher.TRUE, MethodMatcher.TRUE);
MethodMatcher second = new ComposablePointcut(MethodMatcher.TRUE).union(new ComposablePointcut(MethodMatcher.TRUE)).getMethodMatcher();
assertThat(first.equals(second)).isTrue();
assertThat(second.equals(first)).isTrue();
assertThat(first).isEqualTo(second);
assertThat(second).isEqualTo(first);
}
@Test
@@ -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.
@@ -34,7 +34,7 @@ import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifie
* @author Chris Beams
* @since 07.01.2005
*/
public class LazyInitTargetSourceTests {
class LazyInitTargetSourceTests {
private static final Class<?> CLASS = LazyInitTargetSourceTests.class;
@@ -42,9 +42,11 @@ public class LazyInitTargetSourceTests {
private static final Resource CUSTOM_TARGET_CONTEXT = qualifiedResource(CLASS, "customTarget.xml");
private static final Resource FACTORY_BEAN_CONTEXT = qualifiedResource(CLASS, "factoryBean.xml");
private final DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@Test
public void testLazyInitSingletonTargetSource() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
void lazyInitSingletonTargetSource() {
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(SINGLETON_CONTEXT);
bf.preInstantiateSingletons();
@@ -55,8 +57,7 @@ public class LazyInitTargetSourceTests {
}
@Test
public void testCustomLazyInitSingletonTargetSource() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
void customLazyInitSingletonTargetSource() {
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(CUSTOM_TARGET_CONTEXT);
bf.preInstantiateSingletons();
@@ -67,25 +68,25 @@ public class LazyInitTargetSourceTests {
}
@Test
public void testLazyInitFactoryBeanTargetSource() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@SuppressWarnings("unchecked")
void lazyInitFactoryBeanTargetSource() {
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(FACTORY_BEAN_CONTEXT);
bf.preInstantiateSingletons();
Set<?> set1 = (Set<?>) bf.getBean("proxy1");
Set<Object> set1 = (Set<Object>) bf.getBean("proxy1");
assertThat(bf.containsSingleton("target1")).isFalse();
assertThat(set1.contains("10")).isTrue();
assertThat(set1).contains("10");
assertThat(bf.containsSingleton("target1")).isTrue();
Set<?> set2 = (Set<?>) bf.getBean("proxy2");
Set<Object> set2 = (Set<Object>) bf.getBean("proxy2");
assertThat(bf.containsSingleton("target2")).isFalse();
assertThat(set2.contains("20")).isTrue();
assertThat(set2).contains("20");
assertThat(bf.containsSingleton("target2")).isTrue();
}
@SuppressWarnings("serial")
public static class CustomLazyInitTargetSource extends LazyInitTargetSource {
static class CustomLazyInitTargetSource extends LazyInitTargetSource {
@Override
protected void postProcessTargetObject(Object targetObject) {
@@ -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.
@@ -75,14 +75,9 @@ public class PrototypeBasedTargetSourceTests {
private TestBean thisFieldIsNotSerializable = new TestBean();
@Override
public Object getTarget() throws Exception {
public Object getTarget() {
return newPrototypeInstance();
}
@Override
public void releaseTarget(Object target) throws Exception {
// Do nothing
}
}
}
@@ -0,0 +1,75 @@
/*
* 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.aop.framework
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import kotlin.coroutines.Continuation
/**
* Tests for [CoroutinesUtils].
*
* @author Sebastien Deleuze
*/
class CoroutinesUtilsTests {
@Test
fun awaitSingleNonNullValue() {
val value = "foo"
val continuation = Continuation<Any>(CoroutineName("test")) { }
runBlocking {
assertThat(CoroutinesUtils.awaitSingleOrNull(value, continuation)).isEqualTo(value)
}
}
@Test
fun awaitSingleNullValue() {
val value = null
val continuation = Continuation<Any>(CoroutineName("test")) { }
runBlocking {
assertThat(CoroutinesUtils.awaitSingleOrNull(value, continuation)).isNull()
}
}
@Test
fun awaitSingleMonoValue() {
val value = "foo"
val continuation = Continuation<Any>(CoroutineName("test")) { }
runBlocking {
assertThat(CoroutinesUtils.awaitSingleOrNull(Mono.just(value), continuation)).isEqualTo(value)
}
}
@Test
@Suppress("UNCHECKED_CAST")
fun flow() {
val value1 = "foo"
val value2 = "bar"
val values = Flux.just(value1, value2)
val flow = CoroutinesUtils.asFlow(values) as Flow<String>
runBlocking {
assertThat(flow.toList()).containsExactly(value1, value2)
}
}
}
@@ -543,7 +543,7 @@ public abstract class AbstractCacheAnnotationTests {
Object r1 = service.multiConditionalCacheAndEvict(key);
Object r3 = service.multiConditionalCacheAndEvict(key);
assertThat(r1.equals(r3)).isFalse();
assertThat(r1).isNotEqualTo(r3);
assertThat(primary.get(key)).isNull();
Object key2 = 3;
@@ -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.
@@ -55,30 +55,30 @@ public class DefaultListableBeanFactoryBenchmark {
RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class);
switch (this.mode) {
case "simple":
break;
case "dependencyCheck":
case "simple" -> {
}
case "dependencyCheck" -> {
rbd = new RootBeanDefinition(LifecycleBean.class);
rbd.setDependencyCheck(RootBeanDefinition.DEPENDENCY_CHECK_OBJECTS);
this.beanFactory.addBeanPostProcessor(new LifecycleBean.PostProcessor());
break;
case "constructor":
}
case "constructor" -> {
rbd.getConstructorArgumentValues().addGenericArgumentValue("juergen");
rbd.getConstructorArgumentValues().addGenericArgumentValue("99");
break;
case "constructorArgument":
}
case "constructorArgument" -> {
rbd.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference("spouse"));
this.beanFactory.registerBeanDefinition("test", rbd);
this.beanFactory.registerBeanDefinition("spouse", new RootBeanDefinition(TestBean.class));
break;
case "properties":
}
case "properties" -> {
rbd.getPropertyValues().add("name", "juergen");
rbd.getPropertyValues().add("age", "99");
break;
case "resolvedProperties":
}
case "resolvedProperties" -> {
rbd.getPropertyValues().add("spouse", new RuntimeBeanReference("spouse"));
this.beanFactory.registerBeanDefinition("spouse", new RootBeanDefinition(TestBean.class));
break;
}
}
rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
this.beanFactory.registerBeanDefinition("test", rbd);
@@ -976,11 +976,11 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
int length = propertyName.length();
for (int i = startIndex; i < length; i++) {
switch (propertyName.charAt(i)) {
case PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR:
case PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR -> {
// The property name contains opening prefix(es)...
unclosedPrefixes++;
break;
case PropertyAccessor.PROPERTY_KEY_SUFFIX_CHAR:
}
case PropertyAccessor.PROPERTY_KEY_SUFFIX_CHAR -> {
if (unclosedPrefixes == 0) {
// No unclosed prefix(es) in the property name (left) ->
// this is the suffix we are looking for.
@@ -991,13 +991,12 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
// just one that occurred within the property name.
unclosedPrefixes--;
}
break;
}
}
}
return -1;
}
@Override
public String toString() {
String className = getClass().getName();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -91,14 +91,14 @@ public abstract class PropertyAccessorUtils {
int i = (last ? length - 1 : 0);
while (last ? i >= 0 : i < length) {
switch (propertyPath.charAt(i)) {
case PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR:
case PropertyAccessor.PROPERTY_KEY_SUFFIX_CHAR:
case PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR, PropertyAccessor.PROPERTY_KEY_SUFFIX_CHAR -> {
inKey = !inKey;
break;
case PropertyAccessor.NESTED_PROPERTY_SEPARATOR_CHAR:
}
case PropertyAccessor.NESTED_PROPERTY_SEPARATOR_CHAR -> {
if (!inKey) {
return i;
}
}
}
if (last) {
i--;
@@ -49,6 +49,7 @@ import org.springframework.util.StringUtils;
* @author Juergen Hoeller
* @author Rob Harrop
* @author Dave Syer
* @author Yanming Zhou
* @since 2.0
* @see BeanWrapperImpl
* @see SimpleTypeConverter
@@ -178,15 +179,15 @@ class TypeConverterDelegate {
return (T) convertToTypedArray(convertedValue, propertyName, requiredType.componentType());
}
else if (convertedValue.getClass().isArray()) {
if (Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
standardConversion = true;
}
else if (Collection.class.isAssignableFrom(requiredType)) {
if (Collection.class.isAssignableFrom(requiredType)) {
convertedValue = convertToTypedCollection(CollectionUtils.arrayToList(convertedValue),
propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
else if (Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
standardConversion = true;
}
}
else if (convertedValue instanceof Collection<?> coll) {
// Convert elements to target type, if determined.
@@ -159,6 +159,9 @@ import org.springframework.util.StringUtils;
public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
MergedBeanDefinitionPostProcessor, BeanRegistrationAotProcessor, PriorityOrdered, BeanFactoryAware {
private static final Constructor<?>[] EMPTY_CONSTRUCTOR_ARRAY = new Constructor<?>[0];
protected final Log logger = LogFactory.getLog(getClass());
private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);
@@ -286,7 +289,25 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// Register externally managed config members on bean definition.
findInjectionMetadata(beanName, beanType, beanDefinition);
// Use opportunity to clear caches which are not needed after singleton instantiation.
// The injectionMetadataCache itself is left intact since it cannot be reliably
// reconstructed in terms of externally managed config members otherwise.
if (beanDefinition.isSingleton()) {
this.candidateConstructorsCache.remove(beanType);
// With actual lookup overrides, keep it intact along with bean definition.
if (!beanDefinition.hasMethodOverrides()) {
this.lookupMethodsChecked.remove(beanName);
}
}
}
@Override
public void resetBeanDefinition(String beanName) {
this.lookupMethodsChecked.remove(beanName);
this.injectionMetadataCache.remove(beanName);
}
@Override
@@ -324,12 +345,6 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
return metadata;
}
@Override
public void resetBeanDefinition(String beanName) {
this.lookupMethodsChecked.remove(beanName);
this.injectionMetadataCache.remove(beanName);
}
@Override
public Class<?> determineBeanType(Class<?> beanClass, String beanName) throws BeanCreationException {
checkLookupMethods(beanClass, beanName);
@@ -429,7 +444,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
"default constructor to fall back to: " + candidates.get(0));
}
}
candidateConstructors = candidates.toArray(new Constructor<?>[0]);
candidateConstructors = candidates.toArray(EMPTY_CONSTRUCTOR_ARRAY);
}
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
@@ -442,7 +457,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
candidateConstructors = new Constructor<?>[] {primaryConstructor};
}
else {
candidateConstructors = new Constructor<?>[0];
candidateConstructors = EMPTY_CONSTRUCTOR_ARRAY;
}
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
}
@@ -191,16 +191,14 @@ public final class AutowiredFieldValueResolver extends AutowiredElementResolver
return value;
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName,
new InjectionPoint(field), ex);
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
}
private Field getField(RegisteredBean registeredBean) {
Field field = ReflectionUtils.findField(registeredBean.getBeanClass(),
this.fieldName);
Assert.notNull(field, () -> "No field '" + this.fieldName + "' found on "
+ registeredBean.getBeanClass().getName());
Field field = ReflectionUtils.findField(registeredBean.getBeanClass(), this.fieldName);
Assert.notNull(field, () -> "No field '" + this.fieldName + "' found on " +
registeredBean.getBeanClass().getName());
return field;
}
@@ -34,6 +34,9 @@ import java.util.function.Function;
import java.util.function.Predicate;
import org.springframework.aot.generate.GeneratedMethods;
import org.springframework.aot.generate.ValueCodeGenerator;
import org.springframework.aot.generate.ValueCodeGenerator.Delegate;
import org.springframework.aot.generate.ValueCodeGeneratorDelegates;
import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
@@ -89,17 +92,22 @@ class BeanDefinitionPropertiesCodeGenerator {
private final Predicate<String> attributeFilter;
private final BeanDefinitionPropertyValueCodeGenerator valueCodeGenerator;
private final ValueCodeGenerator valueCodeGenerator;
BeanDefinitionPropertiesCodeGenerator(RuntimeHints hints,
Predicate<String> attributeFilter, GeneratedMethods generatedMethods,
List<Delegate> additionalDelegates,
BiFunction<String, Object, CodeBlock> customValueCodeGenerator) {
this.hints = hints;
this.attributeFilter = attributeFilter;
this.valueCodeGenerator = new BeanDefinitionPropertyValueCodeGenerator(generatedMethods,
(object, type) -> customValueCodeGenerator.apply(PropertyNamesStack.peek(), object));
List<Delegate> allDelegates = new ArrayList<>();
allDelegates.add((valueCodeGenerator, value) -> customValueCodeGenerator.apply(PropertyNamesStack.peek(), value));
allDelegates.addAll(additionalDelegates);
allDelegates.addAll(BeanDefinitionPropertyValueCodeGeneratorDelegates.INSTANCES);
allDelegates.addAll(ValueCodeGeneratorDelegates.INSTANCES);
this.valueCodeGenerator = ValueCodeGenerator.with(allDelegates).scoped(generatedMethods);
}
@@ -166,6 +174,10 @@ class BeanDefinitionPropertiesCodeGenerator {
Method method = ReflectionUtils.findMethod(methodDeclaringClass, methodName);
if (method != null) {
this.hints.reflection().registerMethod(method, ExecutableMode.INVOKE);
Method interfaceMethod = ClassUtils.getInterfaceMethodIfPossible(method, beanUserClass);
if (!interfaceMethod.equals(method)) {
this.hints.reflection().registerMethod(interfaceMethod, ExecutableMode.INVOKE);
}
}
}
@@ -209,32 +221,32 @@ class BeanDefinitionPropertiesCodeGenerator {
private void addPropertyValues(CodeBlock.Builder code, RootBeanDefinition beanDefinition) {
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
if (!propertyValues.isEmpty()) {
Class<?> infrastructureType = getInfrastructureType(beanDefinition);
Map<String, Method> writeMethods = (infrastructureType != Object.class) ? getWriteMethods(infrastructureType) : Collections.emptyMap();
for (PropertyValue propertyValue : propertyValues) {
String name = propertyValue.getName();
CodeBlock valueCode = generateValue(name, propertyValue.getValue());
code.addStatement("$L.getPropertyValues().addPropertyValue($S, $L)",
BEAN_DEFINITION_VARIABLE, propertyValue.getName(), valueCode);
}
Class<?> infrastructureType = getInfrastructureType(beanDefinition);
if (infrastructureType != Object.class) {
Map<String, Method> writeMethods = getWriteMethods(infrastructureType);
for (PropertyValue propertyValue : propertyValues) {
Method writeMethod = writeMethods.get(propertyValue.getName());
if (writeMethod != null) {
this.hints.reflection().registerMethod(writeMethod, ExecutableMode.INVOKE);
// ReflectionUtils#findField searches recursively in the type hierarchy
Class<?> searchType = beanDefinition.getTargetType();
while (searchType != null && searchType != writeMethod.getDeclaringClass()) {
this.hints.reflection().registerType(searchType, MemberCategory.DECLARED_FIELDS);
searchType = searchType.getSuperclass();
}
this.hints.reflection().registerType(writeMethod.getDeclaringClass(), MemberCategory.DECLARED_FIELDS);
}
BEAN_DEFINITION_VARIABLE, name, valueCode);
Method writeMethod = writeMethods.get(name);
if (writeMethod != null) {
registerReflectionHints(beanDefinition, writeMethod);
}
}
}
}
private void registerReflectionHints(RootBeanDefinition beanDefinition, Method writeMethod) {
this.hints.reflection().registerMethod(writeMethod, ExecutableMode.INVOKE);
// ReflectionUtils#findField searches recursively in the type hierarchy
Class<?> searchType = beanDefinition.getTargetType();
while (searchType != null && searchType != writeMethod.getDeclaringClass()) {
this.hints.reflection().registerType(searchType, MemberCategory.DECLARED_FIELDS);
searchType = searchType.getSuperclass();
}
this.hints.reflection().registerType(writeMethod.getDeclaringClass(), MemberCategory.DECLARED_FIELDS);
}
private void addQualifiers(CodeBlock.Builder code, RootBeanDefinition beanDefinition) {
Set<AutowireCandidateQualifier> qualifiers = beanDefinition.getQualifiers();
if (!qualifiers.isEmpty()) {
@@ -362,6 +374,7 @@ class BeanDefinitionPropertiesCodeGenerator {
return (castNecessary ? CodeBlock.of("($T) $L", castType, valueCode) : valueCode);
}
static class PropertyNamesStack {
private static final ThreadLocal<ArrayDeque<String>> threadLocal = ThreadLocal.withInitial(ArrayDeque::new);
@@ -380,7 +393,6 @@ class BeanDefinitionPropertiesCodeGenerator {
String value = threadLocal.get().peek();
return ("".equals(value) ? null : value);
}
}
}
@@ -1,600 +0,0 @@
/*
* 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;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.BiFunction;
import java.util.stream.Stream;
import org.springframework.aot.generate.GeneratedMethod;
import org.springframework.aot.generate.GeneratedMethods;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.core.ResolvableType;
import org.springframework.javapoet.AnnotationSpec;
import org.springframework.javapoet.CodeBlock;
import org.springframework.javapoet.CodeBlock.Builder;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* Internal code generator used to generate code for a single value contained in
* a {@link BeanDefinition} property.
*
* @author Stephane Nicoll
* @author Phillip Webb
* @author Sebastien Deleuze
* @since 6.0
*/
class BeanDefinitionPropertyValueCodeGenerator {
static final CodeBlock NULL_VALUE_CODE_BLOCK = CodeBlock.of("null");
private final GeneratedMethods generatedMethods;
private final List<Delegate> delegates;
BeanDefinitionPropertyValueCodeGenerator(GeneratedMethods generatedMethods,
@Nullable BiFunction<Object, ResolvableType, CodeBlock> customValueGenerator) {
this.generatedMethods = generatedMethods;
this.delegates = new ArrayList<>();
if (customValueGenerator != null) {
this.delegates.add(customValueGenerator::apply);
}
this.delegates.addAll(List.of(
new PrimitiveDelegate(),
new StringDelegate(),
new CharsetDelegate(),
new EnumDelegate(),
new ClassDelegate(),
new ResolvableTypeDelegate(),
new ArrayDelegate(),
new ManagedListDelegate(),
new ManagedSetDelegate(),
new ManagedMapDelegate(),
new ListDelegate(),
new SetDelegate(),
new MapDelegate(),
new BeanReferenceDelegate(),
new TypedStringValueDelegate()
));
}
CodeBlock generateCode(@Nullable Object value) {
ResolvableType type = ResolvableType.forInstance(value);
try {
return generateCode(value, type);
}
catch (Exception ex) {
throw new IllegalArgumentException(buildErrorMessage(value, type), ex);
}
}
private CodeBlock generateCodeForElement(@Nullable Object value, ResolvableType type) {
try {
return generateCode(value, type);
}
catch (Exception ex) {
throw new IllegalArgumentException(buildErrorMessage(value, type), ex);
}
}
private static String buildErrorMessage(@Nullable Object value, ResolvableType type) {
StringBuilder message = new StringBuilder("Failed to generate code for '");
message.append(value).append("'");
if (type != ResolvableType.NONE) {
message.append(" with type ").append(type);
}
return message.toString();
}
private CodeBlock generateCode(@Nullable Object value, ResolvableType type) {
if (value == null) {
return NULL_VALUE_CODE_BLOCK;
}
for (Delegate delegate : this.delegates) {
CodeBlock code = delegate.generateCode(value, type);
if (code != null) {
return code;
}
}
throw new IllegalArgumentException("Code generation does not support " + type);
}
/**
* Internal delegate used to support generation for a specific type.
*/
@FunctionalInterface
private interface Delegate {
@Nullable
CodeBlock generateCode(Object value, ResolvableType type);
}
/**
* {@link Delegate} for {@code primitive} types.
*/
private static class PrimitiveDelegate implements Delegate {
private static final Map<Character, String> CHAR_ESCAPES = Map.of(
'\b', "\\b",
'\t', "\\t",
'\n', "\\n",
'\f', "\\f",
'\r', "\\r",
'\"', "\"",
'\'', "\\'",
'\\', "\\\\"
);
@Override
@Nullable
public CodeBlock generateCode(Object value, ResolvableType type) {
if (value instanceof Boolean || value instanceof Integer) {
return CodeBlock.of("$L", value);
}
if (value instanceof Byte) {
return CodeBlock.of("(byte) $L", value);
}
if (value instanceof Short) {
return CodeBlock.of("(short) $L", value);
}
if (value instanceof Long) {
return CodeBlock.of("$LL", value);
}
if (value instanceof Float) {
return CodeBlock.of("$LF", value);
}
if (value instanceof Double) {
return CodeBlock.of("(double) $L", value);
}
if (value instanceof Character character) {
return CodeBlock.of("'$L'", escape(character));
}
return null;
}
private String escape(char ch) {
String escaped = CHAR_ESCAPES.get(ch);
if (escaped != null) {
return escaped;
}
return (!Character.isISOControl(ch)) ? Character.toString(ch)
: String.format("\\u%04x", (int) ch);
}
}
/**
* {@link Delegate} for {@link String} types.
*/
private static class StringDelegate implements Delegate {
@Override
@Nullable
public CodeBlock generateCode(Object value, ResolvableType type) {
if (value instanceof String) {
return CodeBlock.of("$S", value);
}
return null;
}
}
/**
* {@link Delegate} for {@link Charset} types.
*/
private static class CharsetDelegate implements Delegate {
@Override
@Nullable
public CodeBlock generateCode(Object value, ResolvableType type) {
if (value instanceof Charset charset) {
return CodeBlock.of("$T.forName($S)", Charset.class, charset.name());
}
return null;
}
}
/**
* {@link Delegate} for {@link Enum} types.
*/
private static class EnumDelegate implements Delegate {
@Override
@Nullable
public CodeBlock generateCode(Object value, ResolvableType type) {
if (value instanceof Enum<?> enumValue) {
return CodeBlock.of("$T.$L", enumValue.getDeclaringClass(),
enumValue.name());
}
return null;
}
}
/**
* {@link Delegate} for {@link Class} types.
*/
private static class ClassDelegate implements Delegate {
@Override
@Nullable
public CodeBlock generateCode(Object value, ResolvableType type) {
if (value instanceof Class<?> clazz) {
return CodeBlock.of("$T.class", ClassUtils.getUserClass(clazz));
}
return null;
}
}
/**
* {@link Delegate} for {@link ResolvableType} types.
*/
private static class ResolvableTypeDelegate implements Delegate {
@Override
@Nullable
public CodeBlock generateCode(Object value, ResolvableType type) {
if (value instanceof ResolvableType resolvableType) {
return ResolvableTypeCodeGenerator.generateCode(resolvableType);
}
return null;
}
}
/**
* {@link Delegate} for {@code array} types.
*/
private class ArrayDelegate implements Delegate {
@Override
@Nullable
public CodeBlock generateCode(@Nullable Object value, ResolvableType type) {
if (type.isArray()) {
ResolvableType componentType = type.getComponentType();
Stream<CodeBlock> elements = Arrays.stream(ObjectUtils.toObjectArray(value)).map(component ->
BeanDefinitionPropertyValueCodeGenerator.this.generateCode(component, componentType));
CodeBlock.Builder code = CodeBlock.builder();
code.add("new $T {", type.toClass());
code.add(elements.collect(CodeBlock.joining(", ")));
code.add("}");
return code.build();
}
return null;
}
}
/**
* Abstract {@link Delegate} for {@code Collection} types.
*/
private abstract class CollectionDelegate<T extends Collection<?>> implements Delegate {
private final Class<?> collectionType;
private final CodeBlock emptyResult;
public CollectionDelegate(Class<?> collectionType, CodeBlock emptyResult) {
this.collectionType = collectionType;
this.emptyResult = emptyResult;
}
@SuppressWarnings("unchecked")
@Override
@Nullable
public CodeBlock generateCode(Object value, ResolvableType type) {
if (this.collectionType.isInstance(value)) {
T collection = (T) value;
if (collection.isEmpty()) {
return this.emptyResult;
}
ResolvableType elementType = type.as(this.collectionType).getGeneric();
return generateCollectionCode(elementType, collection);
}
return null;
}
protected CodeBlock generateCollectionCode(ResolvableType elementType, T collection) {
return generateCollectionOf(collection, this.collectionType, elementType);
}
protected final CodeBlock generateCollectionOf(Collection<?> collection,
Class<?> collectionType, ResolvableType elementType) {
Builder code = CodeBlock.builder();
code.add("$T.of(", collectionType);
Iterator<?> iterator = collection.iterator();
while (iterator.hasNext()) {
Object element = iterator.next();
code.add("$L", BeanDefinitionPropertyValueCodeGenerator.this
.generateCodeForElement(element, elementType));
if (iterator.hasNext()) {
code.add(", ");
}
}
code.add(")");
return code.build();
}
}
/**
* {@link Delegate} for {@link ManagedList} types.
*/
private class ManagedListDelegate extends CollectionDelegate<ManagedList<?>> {
public ManagedListDelegate() {
super(ManagedList.class, CodeBlock.of("new $T()", ManagedList.class));
}
}
/**
* {@link Delegate} for {@link ManagedSet} types.
*/
private class ManagedSetDelegate extends CollectionDelegate<ManagedSet<?>> {
public ManagedSetDelegate() {
super(ManagedSet.class, CodeBlock.of("new $T()", ManagedSet.class));
}
}
/**
* {@link Delegate} for {@link ManagedMap} types.
*/
private class ManagedMapDelegate implements Delegate {
private static final CodeBlock EMPTY_RESULT = CodeBlock.of("$T.ofEntries()", ManagedMap.class);
@Override
@Nullable
public CodeBlock generateCode(Object value, ResolvableType type) {
if (value instanceof ManagedMap<?, ?> managedMap) {
return generateManagedMapCode(type, managedMap);
}
return null;
}
private <K, V> CodeBlock generateManagedMapCode(ResolvableType type, ManagedMap<K, V> managedMap) {
if (managedMap.isEmpty()) {
return EMPTY_RESULT;
}
ResolvableType keyType = type.as(Map.class).getGeneric(0);
ResolvableType valueType = type.as(Map.class).getGeneric(1);
CodeBlock.Builder code = CodeBlock.builder();
code.add("$T.ofEntries(", ManagedMap.class);
Iterator<Map.Entry<K, V>> iterator = managedMap.entrySet().iterator();
while (iterator.hasNext()) {
Entry<?, ?> entry = iterator.next();
code.add("$T.entry($L,$L)", Map.class,
BeanDefinitionPropertyValueCodeGenerator.this
.generateCodeForElement(entry.getKey(), keyType),
BeanDefinitionPropertyValueCodeGenerator.this
.generateCodeForElement(entry.getValue(), valueType));
if (iterator.hasNext()) {
code.add(", ");
}
}
code.add(")");
return code.build();
}
}
/**
* {@link Delegate} for {@link List} types.
*/
private class ListDelegate extends CollectionDelegate<List<?>> {
ListDelegate() {
super(List.class, CodeBlock.of("$T.emptyList()", Collections.class));
}
}
/**
* {@link Delegate} for {@link Set} types.
*/
private class SetDelegate extends CollectionDelegate<Set<?>> {
SetDelegate() {
super(Set.class, CodeBlock.of("$T.emptySet()", Collections.class));
}
@Override
protected CodeBlock generateCollectionCode(ResolvableType elementType, Set<?> set) {
if (set instanceof LinkedHashSet) {
return CodeBlock.of("new $T($L)", LinkedHashSet.class,
generateCollectionOf(set, List.class, elementType));
}
return super.generateCollectionCode(elementType, orderForCodeConsistency(set));
}
private Set<?> orderForCodeConsistency(Set<?> set) {
try {
return new TreeSet<Object>(set);
}
catch (ClassCastException ex) {
// If elements are not comparable, just keep the original set
return set;
}
}
}
/**
* {@link Delegate} for {@link Map} types.
*/
private class MapDelegate implements Delegate {
private static final CodeBlock EMPTY_RESULT = CodeBlock.of("$T.emptyMap()", Collections.class);
@Override
@Nullable
public CodeBlock generateCode(Object value, ResolvableType type) {
if (value instanceof Map<?, ?> map) {
return generateMapCode(type, map);
}
return null;
}
private <K, V> CodeBlock generateMapCode(ResolvableType type, Map<K, V> map) {
if (map.isEmpty()) {
return EMPTY_RESULT;
}
ResolvableType keyType = type.as(Map.class).getGeneric(0);
ResolvableType valueType = type.as(Map.class).getGeneric(1);
if (map instanceof LinkedHashMap<?, ?>) {
return generateLinkedHashMapCode(map, keyType, valueType);
}
map = orderForCodeConsistency(map);
boolean useOfEntries = map.size() > 10;
CodeBlock.Builder code = CodeBlock.builder();
code.add("$T" + ((!useOfEntries) ? ".of(" : ".ofEntries("), Map.class);
Iterator<Map.Entry<K, V>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Entry<K, V> entry = iterator.next();
CodeBlock keyCode = BeanDefinitionPropertyValueCodeGenerator.this
.generateCodeForElement(entry.getKey(), keyType);
CodeBlock valueCode = BeanDefinitionPropertyValueCodeGenerator.this
.generateCodeForElement(entry.getValue(), valueType);
if (!useOfEntries) {
code.add("$L, $L", keyCode, valueCode);
}
else {
code.add("$T.entry($L,$L)", Map.class, keyCode, valueCode);
}
if (iterator.hasNext()) {
code.add(", ");
}
}
code.add(")");
return code.build();
}
private <K, V> Map<K, V> orderForCodeConsistency(Map<K, V> map) {
try {
return new TreeMap<>(map);
}
catch (ClassCastException ex) {
// If elements are not comparable, just keep the original map
return map;
}
}
private <K, V> CodeBlock generateLinkedHashMapCode(Map<K, V> map,
ResolvableType keyType, ResolvableType valueType) {
GeneratedMethods generatedMethods = BeanDefinitionPropertyValueCodeGenerator.this.generatedMethods;
GeneratedMethod generatedMethod = generatedMethods.add("getMap", method -> {
method.addAnnotation(AnnotationSpec
.builder(SuppressWarnings.class)
.addMember("value", "{\"rawtypes\", \"unchecked\"}")
.build());
method.returns(Map.class);
method.addStatement("$T map = new $T($L)", Map.class,
LinkedHashMap.class, map.size());
map.forEach((key, value) -> method.addStatement("map.put($L, $L)",
BeanDefinitionPropertyValueCodeGenerator.this
.generateCodeForElement(key, keyType),
BeanDefinitionPropertyValueCodeGenerator.this
.generateCodeForElement(value, valueType)));
method.addStatement("return map");
});
return CodeBlock.of("$L()", generatedMethod.getName());
}
}
/**
* {@link Delegate} for {@link BeanReference} types.
*/
private static class BeanReferenceDelegate implements Delegate {
@Override
@Nullable
public CodeBlock generateCode(Object value, ResolvableType type) {
if (value instanceof RuntimeBeanReference runtimeBeanReference &&
runtimeBeanReference.getBeanType() != null) {
return CodeBlock.of("new $T($T.class)", RuntimeBeanReference.class,
runtimeBeanReference.getBeanType());
}
else if (value instanceof BeanReference beanReference) {
return CodeBlock.of("new $T($S)", RuntimeBeanReference.class,
beanReference.getBeanName());
}
return null;
}
}
/**
* {@link Delegate} for {@link TypedStringValue} types.
*/
private class TypedStringValueDelegate implements Delegate {
@Override
public CodeBlock generateCode(Object value, ResolvableType type) {
if (value instanceof TypedStringValue typedStringValue) {
return generateTypeStringValueCode(typedStringValue);
}
return null;
}
private CodeBlock generateTypeStringValueCode(TypedStringValue typedStringValue) {
String value = typedStringValue.getValue();
if (typedStringValue.hasTargetType()) {
return CodeBlock.of("new $T($S, $L)", TypedStringValue.class, value,
generateCode(typedStringValue.getTargetType()));
}
return generateCode(value);
}
private CodeBlock generateCode(@Nullable Object value) {
return BeanDefinitionPropertyValueCodeGenerator.this.generateCode(value);
}
}
}
@@ -0,0 +1,212 @@
/*
* 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;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.aot.generate.GeneratedMethod;
import org.springframework.aot.generate.GeneratedMethods;
import org.springframework.aot.generate.ValueCodeGenerator;
import org.springframework.aot.generate.ValueCodeGenerator.Delegate;
import org.springframework.aot.generate.ValueCodeGeneratorDelegates;
import org.springframework.aot.generate.ValueCodeGeneratorDelegates.CollectionDelegate;
import org.springframework.aot.generate.ValueCodeGeneratorDelegates.MapDelegate;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.javapoet.AnnotationSpec;
import org.springframework.javapoet.CodeBlock;
/**
* Code generator {@link Delegate} for common bean definition property values.
*
* @author Stephane Nicoll
* @since 6.1.2
*/
abstract class BeanDefinitionPropertyValueCodeGeneratorDelegates {
/**
* Return the {@link Delegate} implementations for common bean definition
* property value types. These are:
* <ul>
* <li>{@link ManagedList},</li>
* <li>{@link ManagedSet},</li>
* <li>{@link ManagedMap},</li>
* <li>{@link LinkedHashMap},</li>
* <li>{@link BeanReference},</li>
* <li>{@link TypedStringValue}.</li>
* </ul>
* When combined with {@linkplain ValueCodeGeneratorDelegates#INSTANCES the
* delegates for common value types}, this should be added first as they have
* special handling for list, set, and map.
*/
public static final List<Delegate> INSTANCES = List.of(
new ManagedListDelegate(),
new ManagedSetDelegate(),
new ManagedMapDelegate(),
new LinkedHashMapDelegate(),
new BeanReferenceDelegate(),
new TypedStringValueDelegate()
);
/**
* {@link Delegate} for {@link ManagedList} types.
*/
private static class ManagedListDelegate extends CollectionDelegate<ManagedList<?>> {
public ManagedListDelegate() {
super(ManagedList.class, CodeBlock.of("new $T()", ManagedList.class));
}
}
/**
* {@link Delegate} for {@link ManagedSet} types.
*/
private static class ManagedSetDelegate extends CollectionDelegate<ManagedSet<?>> {
public ManagedSetDelegate() {
super(ManagedSet.class, CodeBlock.of("new $T()", ManagedSet.class));
}
}
/**
* {@link Delegate} for {@link ManagedMap} types.
*/
private static class ManagedMapDelegate implements Delegate {
private static final CodeBlock EMPTY_RESULT = CodeBlock.of("$T.ofEntries()", ManagedMap.class);
@Override
public CodeBlock generateCode(ValueCodeGenerator valueCodeGenerator, Object value) {
if (value instanceof ManagedMap<?, ?> managedMap) {
return generateManagedMapCode(valueCodeGenerator, managedMap);
}
return null;
}
private <K, V> CodeBlock generateManagedMapCode(ValueCodeGenerator valueCodeGenerator,
ManagedMap<K, V> managedMap) {
if (managedMap.isEmpty()) {
return EMPTY_RESULT;
}
CodeBlock.Builder code = CodeBlock.builder();
code.add("$T.ofEntries(", ManagedMap.class);
Iterator<Entry<K, V>> iterator = managedMap.entrySet().iterator();
while (iterator.hasNext()) {
Entry<?, ?> entry = iterator.next();
code.add("$T.entry($L,$L)", Map.class,
valueCodeGenerator.generateCode(entry.getKey()),
valueCodeGenerator.generateCode(entry.getValue()));
if (iterator.hasNext()) {
code.add(", ");
}
}
code.add(")");
return code.build();
}
}
/**
* {@link Delegate} for {@link Map} types.
*/
private static class LinkedHashMapDelegate extends MapDelegate {
@Override
protected CodeBlock generateMapCode(ValueCodeGenerator valueCodeGenerator, Map<?, ?> map) {
GeneratedMethods generatedMethods = valueCodeGenerator.getGeneratedMethods();
if (map instanceof LinkedHashMap<?, ?> && generatedMethods != null) {
return generateLinkedHashMapCode(valueCodeGenerator, generatedMethods, map);
}
return super.generateMapCode(valueCodeGenerator, map);
}
private CodeBlock generateLinkedHashMapCode(ValueCodeGenerator valueCodeGenerator,
GeneratedMethods generatedMethods, Map<?, ?> map) {
GeneratedMethod generatedMethod = generatedMethods.add("getMap", method -> {
method.addAnnotation(AnnotationSpec
.builder(SuppressWarnings.class)
.addMember("value", "{\"rawtypes\", \"unchecked\"}")
.build());
method.returns(Map.class);
method.addStatement("$T map = new $T($L)", Map.class,
LinkedHashMap.class, map.size());
map.forEach((key, value) -> method.addStatement("map.put($L, $L)",
valueCodeGenerator.generateCode(key),
valueCodeGenerator.generateCode(value)));
method.addStatement("return map");
});
return CodeBlock.of("$L()", generatedMethod.getName());
}
}
/**
* {@link Delegate} for {@link BeanReference} types.
*/
private static class BeanReferenceDelegate implements Delegate {
@Override
public CodeBlock generateCode(ValueCodeGenerator valueCodeGenerator, Object value) {
if (value instanceof RuntimeBeanReference runtimeBeanReference &&
runtimeBeanReference.getBeanType() != null) {
return CodeBlock.of("new $T($T.class)", RuntimeBeanReference.class,
runtimeBeanReference.getBeanType());
}
else if (value instanceof BeanReference beanReference) {
return CodeBlock.of("new $T($S)", RuntimeBeanReference.class,
beanReference.getBeanName());
}
return null;
}
}
/**
* {@link Delegate} for {@link TypedStringValue} types.
*/
private static class TypedStringValueDelegate implements Delegate {
@Override
public CodeBlock generateCode(ValueCodeGenerator valueCodeGenerator, Object value) {
if (value instanceof TypedStringValue typedStringValue) {
return generateTypeStringValueCode(valueCodeGenerator, typedStringValue);
}
return null;
}
private CodeBlock generateTypeStringValueCode(ValueCodeGenerator valueCodeGenerator, TypedStringValue typedStringValue) {
String value = typedStringValue.getValue();
if (typedStringValue.hasTargetType()) {
return CodeBlock.of("new $T($S, $L)", TypedStringValue.class, value,
valueCodeGenerator.generateCode(typedStringValue.getTargetType()));
}
return valueCodeGenerator.generateCode(value);
}
}
}
@@ -27,7 +27,10 @@ import org.springframework.aot.generate.AccessControl;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.generate.MethodReference;
import org.springframework.aot.generate.MethodReference.ArgumentCodeGenerator;
import org.springframework.aot.generate.ValueCodeGenerator;
import org.springframework.aot.generate.ValueCodeGenerator.Delegate;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.aot.AotServices.Loader;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.InstanceSupplier;
@@ -51,6 +54,8 @@ import org.springframework.util.function.SingletonSupplier;
*/
class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragments {
private static final ValueCodeGenerator valueCodeGenerator = ValueCodeGenerator.withDefaults();
private final BeanRegistrationsCode beanRegistrationsCode;
private final RegisteredBean registeredBean;
@@ -147,9 +152,9 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
private CodeBlock generateBeanTypeCode(ResolvableType beanType) {
if (!beanType.hasGenerics()) {
return CodeBlock.of("$T.class", ClassUtils.getUserClass(beanType.toClass()));
return valueCodeGenerator.generateCode(ClassUtils.getUserClass(beanType.toClass()));
}
return ResolvableTypeCodeGenerator.generateCode(beanType);
return valueCodeGenerator.generateCode(beanType);
}
private boolean targetTypeNecessary(ResolvableType beanType, @Nullable Class<?> beanClass) {
@@ -168,12 +173,12 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
GenerationContext generationContext,
BeanRegistrationCode beanRegistrationCode, RootBeanDefinition beanDefinition,
Predicate<String> attributeFilter) {
return new BeanDefinitionPropertiesCodeGenerator(
generationContext.getRuntimeHints(), attributeFilter,
beanRegistrationCode.getMethods(),
(name, value) -> generateValueCode(generationContext, name, value))
.generateCode(beanDefinition);
Loader loader = AotServices.factories(this.registeredBean.getBeanFactory().getBeanClassLoader());
List<Delegate> additionalDelegates = loader.load(Delegate.class).asList();
return new BeanDefinitionPropertiesCodeGenerator(generationContext.getRuntimeHints(),
attributeFilter, beanRegistrationCode.getMethods(),
additionalDelegates, (name, value) -> generateValueCode(generationContext, name, value)
).generateCode(beanDefinition);
}
@Nullable
@@ -1,69 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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;
import java.util.Arrays;
import org.springframework.core.ResolvableType;
import org.springframework.javapoet.CodeBlock;
import org.springframework.util.ClassUtils;
/**
* Internal code generator used to support {@link ResolvableType}.
*
* @author Stephane Nicoll
* @author Phillip Webb
* @since 6.0
*/
final class ResolvableTypeCodeGenerator {
private ResolvableTypeCodeGenerator() {
}
public static CodeBlock generateCode(ResolvableType resolvableType) {
return generateCode(resolvableType, false);
}
private static CodeBlock generateCode(ResolvableType resolvableType, boolean allowClassResult) {
if (ResolvableType.NONE.equals(resolvableType)) {
return CodeBlock.of("$T.NONE", ResolvableType.class);
}
Class<?> type = ClassUtils.getUserClass(resolvableType.toClass());
if (resolvableType.hasGenerics() && !resolvableType.hasUnresolvableGenerics()) {
return generateCodeWithGenerics(resolvableType, type);
}
if (allowClassResult) {
return CodeBlock.of("$T.class", type);
}
return CodeBlock.of("$T.forClass($T.class)", ResolvableType.class, type);
}
private static CodeBlock generateCodeWithGenerics(ResolvableType target, Class<?> type) {
ResolvableType[] generics = target.getGenerics();
boolean hasNoNestedGenerics = Arrays.stream(generics).noneMatch(ResolvableType::hasGenerics);
CodeBlock.Builder code = CodeBlock.builder();
code.add("$T.forClassWithGenerics($T.class", ResolvableType.class, type);
for (ResolvableType generic : generics) {
code.add(", $L", generateCode(generic, hasNoNestedGenerics));
}
code.add(")");
return code.build();
}
}
@@ -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.
@@ -375,6 +375,16 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
}
}
/**
* Determine whether this dependency supports lazy resolution,
* e.g. through extra proxying. The default is {@code true}.
* @since 6.1.2
* @see org.springframework.beans.factory.support.AutowireCandidateResolver#getLazyResolutionProxyIfNecessary
*/
public boolean supportsLazyResolution() {
return true;
}
@Override
public boolean equals(@Nullable Object other) {
@@ -496,15 +496,13 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
}
try {
@@ -51,6 +51,7 @@ import org.springframework.util.StringUtils;
* @author Juergen Hoeller
* @author Rob Harrop
* @author Mark Fisher
* @author Sebastien Deleuze
* @see GenericBeanDefinition
* @see RootBeanDefinition
* @see ChildBeanDefinition
@@ -139,6 +140,18 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
*/
public static final String PREFERRED_CONSTRUCTORS_ATTRIBUTE = "preferredConstructors";
/**
* The name of an attribute that can be
* {@link org.springframework.core.AttributeAccessor#setAttribute set} on a
* {@link org.springframework.beans.factory.config.BeanDefinition} so that
* bean definitions can indicate the sort order for the targeted bean.
* This is analogous to the {@code @Order} annotation.
* @since 6.1.2
* @see org.springframework.core.annotation.Order
* @see org.springframework.core.Ordered
*/
public static final String ORDER_ATTRIBUTE = "order";
/**
* Constant that indicates the container should attempt to infer the
* {@link #setDestroyMethodName destroy method name} for a bean as opposed to
@@ -1503,7 +1503,11 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
if (mbd.hasBeanClass()) {
return mbd.getBeanClass();
}
return doResolveBeanClass(mbd, typesToMatch);
Class<?> beanClass = doResolveBeanClass(mbd, typesToMatch);
if (mbd.hasBeanClass()) {
mbd.prepareMethodOverrides();
}
return beanClass;
}
catch (ClassNotFoundException ex) {
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
@@ -1511,6 +1515,10 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
catch (LinkageError err) {
throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), err);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbd.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
}
@Nullable
@@ -70,6 +70,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.NamedBeanHolder;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
@@ -110,6 +111,7 @@ import org.springframework.util.StringUtils;
* @author Chris Beams
* @author Phillip Webb
* @author Stephane Nicoll
* @author Sebastien Deleuze
* @since 16 April 2001
* @see #registerBeanDefinition
* @see #addBeanPostProcessor
@@ -1341,14 +1343,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
else if (descriptor.supportsLazyResolution()) {
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
if (result != null) {
return result;
}
return result;
}
return doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
@Nullable
@@ -2230,7 +2232,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
* that is aware of the bean metadata of the instances to sort.
* <p>Lookup for the method factory of an instance to sort, if any, and let the
* comparator retrieve the {@link org.springframework.core.annotation.Order}
* value defined on it. This essentially allows for the following construct:
* value defined on it.
* <p>As of 6.1.2, this class takes the {@link AbstractBeanDefinition#ORDER_ATTRIBUTE}
* attribute into account.
*/
private class FactoryAwareOrderSourceProvider implements OrderComparator.OrderSourceProvider {
@@ -2249,7 +2253,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
try {
RootBeanDefinition beanDefinition = (RootBeanDefinition) getMergedBeanDefinition(beanName);
List<Object> sources = new ArrayList<>(2);
List<Object> sources = new ArrayList<>(3);
Object orderAttribute = beanDefinition.getAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE);
if (orderAttribute != null) {
if (orderAttribute instanceof Integer order) {
sources.add((Ordered) () -> order);
}
else {
throw new IllegalStateException("Invalid value type for attribute '" +
AbstractBeanDefinition.ORDER_ATTRIBUTE + "': " + orderAttribute.getClass().getName());
}
}
Method factoryMethod = beanDefinition.getResolvedFactoryMethod();
if (factoryMethod != null) {
sources.add(factoryMethod);
@@ -72,7 +72,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
synchronized (bd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
@@ -105,7 +105,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
final Constructor<?> ctor, Object... args) {
Constructor<?> ctor, Object... args) {
if (!bd.hasMethodOverrides()) {
return BeanUtils.instantiateClass(ctor, args);
@@ -129,7 +129,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
@Nullable Object factoryBean, final Method factoryMethod, Object... args) {
@Nullable Object factoryBean, Method factoryMethod, Object... args) {
try {
ReflectionUtils.makeAccessible(factoryMethod);
@@ -153,8 +153,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
}
}
catch (IllegalArgumentException ex) {
if (factoryBean != null
&& !factoryMethod.getDeclaringClass().isAssignableFrom(factoryBean.getClass())) {
if (factoryBean != null && !factoryMethod.getDeclaringClass().isAssignableFrom(factoryBean.getClass())) {
throw new BeanInstantiationException(factoryMethod,
"Illegal factory instance for factory method '" + factoryMethod.getName() + "'; " +
"instance: " + factoryBean.getClass().getName(), ex);
@@ -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.
@@ -78,8 +78,10 @@ public class PathEditor extends PropertyEditorSupport {
if (nioPathCandidate && !text.startsWith("/")) {
try {
URI uri = ResourceUtils.toURI(text);
if (uri.getScheme() != null) {
nioPathCandidate = false;
String scheme = uri.getScheme();
if (scheme != null) {
// No NIO candidate except for "C:" style drive letters
nioPathCandidate = (scheme.length() == 1);
// Let's try NIO file system providers via Paths.get(URI)
setValue(Paths.get(uri).normalize());
return;
@@ -109,7 +111,8 @@ public class PathEditor extends PropertyEditorSupport {
setValue(resource.getFile().toPath());
}
catch (IOException ex) {
throw new IllegalArgumentException("Failed to retrieve file for " + resource, ex);
throw new IllegalArgumentException(
"Could not retrieve file for " + resource + ": " + ex.getMessage());
}
}
}
@@ -406,6 +406,7 @@ abstract class AbstractPropertyAccessorTests {
}
@Test
@SuppressWarnings("unchecked")
void setPropertyIntermediateListIsNullWithAutoGrow() {
Foo target = new Foo();
AbstractPropertyAccessor accessor = createAccessor(target);
@@ -414,7 +415,7 @@ abstract class AbstractPropertyAccessorTests {
Map<String, String> map = new HashMap<>();
map.put("favoriteNumber", "9");
accessor.setPropertyValue("list[0]", map);
assertThat(target.list.get(0)).isEqualTo(map);
assertThat(target.list).element(0).isEqualTo(map);
}
@Test
@@ -1143,12 +1144,12 @@ abstract class AbstractPropertyAccessorTests {
assertThat(target.getSet()).hasSize(1);
assertThat(target.getSet().contains("set1")).isTrue();
assertThat(target.getSortedSet()).hasSize(1);
assertThat(target.getSortedSet().contains("sortedSet1")).isTrue();
assertThat(target.getSortedSet()).contains("sortedSet1");
assertThat(target.getList()).hasSize(1);
assertThat(target.getList().contains("list1")).isTrue();
assertThat(target.getList()).contains("list1");
accessor.setPropertyValue("list", Collections.singletonList("list1 "));
assertThat(target.getList().contains("list1")).isTrue();
assertThat(target.getList()).contains("list1");
}
@Test
@@ -1,55 +0,0 @@
/*
* 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;
import java.util.HashMap;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rod Johnson
* @author Chris Beams
*/
public abstract class AbstractPropertyValuesTests {
/**
* Must contain: forname=Tony surname=Blair age=50
*/
protected void doTestTony(PropertyValues pvs) {
assertThat(pvs.getPropertyValues()).as("Contains 3").hasSize(3);
assertThat(pvs.contains("forname")).as("Contains forname").isTrue();
assertThat(pvs.contains("surname")).as("Contains surname").isTrue();
assertThat(pvs.contains("age")).as("Contains age").isTrue();
assertThat(!pvs.contains("tory")).as("Doesn't contain tory").isTrue();
PropertyValue[] ps = pvs.getPropertyValues();
Map<String, String> m = new HashMap<>();
m.put("forname", "Tony");
m.put("surname", "Blair");
m.put("age", "50");
for (PropertyValue element : ps) {
Object val = m.get(element.getName());
assertThat(val).as("Can't have unexpected value").isNotNull();
assertThat(val instanceof String).as("Val i string").isTrue();
assertThat(val.equals(element.getValue())).as("val matches expected").isTrue();
m.remove(element.getName());
}
assertThat(m).as("Map size is 0").isEmpty();
}
}
@@ -126,7 +126,7 @@ public class BeanWrapperAutoGrowingTests {
public void getPropertyValueAutoGrowList() {
assertNotNull(wrapper.getPropertyValue("list[0]"));
assertThat(bean.getList()).hasSize(1);
assertThat(bean.getList().get(0)).isInstanceOf(Bean.class);
assertThat(bean.getList()).element(0).isInstanceOf(Bean.class);
}
@Test
@@ -139,11 +139,11 @@ public class BeanWrapperAutoGrowingTests {
public void getPropertyValueAutoGrowListBySeveralElements() {
assertNotNull(wrapper.getPropertyValue("list[4]"));
assertThat(bean.getList()).hasSize(5);
assertThat(bean.getList().get(0)).isInstanceOf(Bean.class);
assertThat(bean.getList().get(1)).isInstanceOf(Bean.class);
assertThat(bean.getList().get(2)).isInstanceOf(Bean.class);
assertThat(bean.getList().get(3)).isInstanceOf(Bean.class);
assertThat(bean.getList().get(4)).isInstanceOf(Bean.class);
assertThat(bean.getList()).element(0).isInstanceOf(Bean.class);
assertThat(bean.getList()).element(1).isInstanceOf(Bean.class);
assertThat(bean.getList()).element(2).isInstanceOf(Bean.class);
assertThat(bean.getList()).element(3).isInstanceOf(Bean.class);
assertThat(bean.getList()).element(4).isInstanceOf(Bean.class);
assertNotNull(wrapper.getPropertyValue("list[0]"));
assertNotNull(wrapper.getPropertyValue("list[1]"));
assertNotNull(wrapper.getPropertyValue("list[2]"));
@@ -161,8 +161,8 @@ public class BeanWrapperAutoGrowingTests {
@Test
public void getPropertyValueAutoGrowMultiDimensionalList() {
assertNotNull(wrapper.getPropertyValue("multiList[0][0]"));
assertThat(bean.getMultiList().get(0)).hasSize(1);
assertThat(bean.getMultiList().get(0).get(0)).isInstanceOf(Bean.class);
assertThat(bean.getMultiList()).element(0).asList().hasSize(1);
assertThat(bean.getMultiList().get(0)).element(0).isInstanceOf(Bean.class);
}
@Test
@@ -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.
@@ -92,7 +92,7 @@ public class BeanWrapperEnumTests {
BeanWrapper bw = new BeanWrapperImpl(gb);
bw.setPropertyValue("customEnumSet", "VALUE_1");
assertThat(gb.getCustomEnumSet()).hasSize(1);
assertThat(gb.getCustomEnumSet().contains(CustomEnum.VALUE_1)).isTrue();
assertThat(gb.getCustomEnumSet()).contains(CustomEnum.VALUE_1);
}
@Test
@@ -101,8 +101,8 @@ public class BeanWrapperEnumTests {
BeanWrapper bw = new BeanWrapperImpl(gb);
bw.setPropertyValue("customEnumSet", new String[] {"VALUE_1", "VALUE_2"});
assertThat(gb.getCustomEnumSet()).hasSize(2);
assertThat(gb.getCustomEnumSet().contains(CustomEnum.VALUE_1)).isTrue();
assertThat(gb.getCustomEnumSet().contains(CustomEnum.VALUE_2)).isTrue();
assertThat(gb.getCustomEnumSet()).contains(CustomEnum.VALUE_1);
assertThat(gb.getCustomEnumSet()).contains(CustomEnum.VALUE_2);
}
@Test
@@ -111,8 +111,8 @@ public class BeanWrapperEnumTests {
BeanWrapper bw = new BeanWrapperImpl(gb);
bw.setPropertyValue("customEnumSet", "VALUE_1,VALUE_2");
assertThat(gb.getCustomEnumSet()).hasSize(2);
assertThat(gb.getCustomEnumSet().contains(CustomEnum.VALUE_1)).isTrue();
assertThat(gb.getCustomEnumSet().contains(CustomEnum.VALUE_2)).isTrue();
assertThat(gb.getCustomEnumSet()).contains(CustomEnum.VALUE_1);
assertThat(gb.getCustomEnumSet()).contains(CustomEnum.VALUE_2);
}
@Test
@@ -121,8 +121,8 @@ public class BeanWrapperEnumTests {
BeanWrapper bw = new BeanWrapperImpl(gb);
bw.setPropertyValue("customEnumSetMismatch", new String[] {"VALUE_1", "VALUE_2"});
assertThat(gb.getCustomEnumSet()).hasSize(2);
assertThat(gb.getCustomEnumSet().contains(CustomEnum.VALUE_1)).isTrue();
assertThat(gb.getCustomEnumSet().contains(CustomEnum.VALUE_2)).isTrue();
assertThat(gb.getCustomEnumSet()).contains(CustomEnum.VALUE_1);
assertThat(gb.getCustomEnumSet()).contains(CustomEnum.VALUE_2);
}
@Test
@@ -133,8 +133,8 @@ public class BeanWrapperEnumTests {
assertThat(gb.getStandardEnumSet()).isNull();
bw.setPropertyValue("standardEnumSet", new String[] {"VALUE_1", "VALUE_2"});
assertThat(gb.getStandardEnumSet()).hasSize(2);
assertThat(gb.getStandardEnumSet().contains(CustomEnum.VALUE_1)).isTrue();
assertThat(gb.getStandardEnumSet().contains(CustomEnum.VALUE_2)).isTrue();
assertThat(gb.getStandardEnumSet()).contains(CustomEnum.VALUE_1);
assertThat(gb.getStandardEnumSet()).contains(CustomEnum.VALUE_2);
}
@Test
@@ -39,6 +39,7 @@ import org.springframework.core.io.UrlResource;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import static java.util.Map.entry;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -57,8 +58,8 @@ class BeanWrapperGenericsTests {
input.add("4");
input.add("5");
bw.setPropertyValue("integerSet", input);
assertThat(gb.getIntegerSet().contains(4)).isTrue();
assertThat(gb.getIntegerSet().contains(5)).isTrue();
assertThat(gb.getIntegerSet()).contains(4);
assertThat(gb.getIntegerSet()).contains(5);
}
@Test
@@ -93,8 +94,8 @@ class BeanWrapperGenericsTests {
input.add("http://localhost:8080");
input.add("http://localhost:9090");
bw.setPropertyValue("resourceList", input);
assertThat(gb.getResourceList().get(0)).isEqualTo(new UrlResource("http://localhost:8080"));
assertThat(gb.getResourceList().get(1)).isEqualTo(new UrlResource("http://localhost:9090"));
assertThat(gb.getResourceList()).element(0).isEqualTo(new UrlResource("http://localhost:8080"));
assertThat(gb.getResourceList()).element(1).isEqualTo(new UrlResource("http://localhost:9090"));
}
@Test
@@ -103,7 +104,7 @@ class BeanWrapperGenericsTests {
gb.setResourceList(new ArrayList<>());
BeanWrapper bw = new BeanWrapperImpl(gb);
bw.setPropertyValue("resourceList[0]", "http://localhost:8080");
assertThat(gb.getResourceList().get(0)).isEqualTo(new UrlResource("http://localhost:8080"));
assertThat(gb.getResourceList()).element(0).isEqualTo(new UrlResource("http://localhost:8080"));
}
@Test
@@ -200,7 +201,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(gb);
bw.setPropertyValue("listOfLists[0][0]", 5);
assertThat(bw.getPropertyValue("listOfLists[0][0]")).isEqualTo(5);
assertThat(gb.getListOfLists().get(0).get(0)).isEqualTo(5);
assertThat(gb.getListOfLists().get(0)).element(0).isEqualTo(5);
}
@Test
@@ -212,7 +213,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(gb);
bw.setPropertyValue("listOfLists[0][0]", "5");
assertThat(bw.getPropertyValue("listOfLists[0][0]")).isEqualTo(5);
assertThat(gb.getListOfLists().get(0).get(0)).isEqualTo(5);
assertThat(gb.getListOfLists().get(0)).element(0).isEqualTo(5);
}
@Test
@@ -297,7 +298,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(gb);
bw.setPropertyValue("mapOfLists[1][0]", 5);
assertThat(bw.getPropertyValue("mapOfLists[1][0]")).isEqualTo(5);
assertThat(gb.getMapOfLists().get(1).get(0)).isEqualTo(5);
assertThat(gb.getMapOfLists().get(1)).element(0).isEqualTo(5);
}
@Test
@@ -309,7 +310,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(gb);
bw.setPropertyValue("mapOfLists[1][0]", "5");
assertThat(bw.getPropertyValue("mapOfLists[1][0]")).isEqualTo(5);
assertThat(gb.getMapOfLists().get(1).get(0)).isEqualTo(5);
assertThat(gb.getMapOfLists().get(1)).element(0).isEqualTo(5);
}
@Test
@@ -384,8 +385,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(holder);
bw.setPropertyValue("genericMap", inputMap);
assertThat(holder.getGenericMap().keySet().iterator().next().get(0)).isEqualTo(1);
assertThat(holder.getGenericMap().values().iterator().next().get(0)).isEqualTo(Long.valueOf(10));
assertThat(holder.getGenericMap()).containsOnly(entry(List.of(1), List.of(10L)));
}
@Test
@@ -401,8 +401,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(holder);
bw.setPropertyValue("genericMap", inputMap);
assertThat(holder.getGenericMap().keySet().iterator().next().get(0)).isEqualTo(1);
assertThat(holder.getGenericMap().values().iterator().next().get(0)).isEqualTo(Long.valueOf(10));
assertThat(holder.getGenericMap()).containsOnly(entry(List.of(1), List.of(10L)));
}
@Test
@@ -414,8 +413,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(holder);
bw.setPropertyValue("genericIndexedMap[1]", inputValue);
assertThat(holder.getGenericIndexedMap().keySet().iterator().next()).isEqualTo(1);
assertThat(holder.getGenericIndexedMap().values().iterator().next().get(0)).isEqualTo(Long.valueOf(10));
assertThat(holder.getGenericIndexedMap()).containsOnly(entry(1, List.of(10L)));
}
@Test
@@ -427,8 +425,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(holder);
bw.setPropertyValue("genericIndexedMap[1]", inputValue);
assertThat(holder.getGenericIndexedMap().keySet().iterator().next()).isEqualTo(1);
assertThat(holder.getGenericIndexedMap().values().iterator().next().get(0)).isEqualTo(Long.valueOf(10));
assertThat(holder.getGenericIndexedMap()).containsOnly(entry(1, List.of(10L)));
}
@Test
@@ -439,8 +436,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(holder);
bw.setPropertyValue("genericIndexedMap[1]", inputValue);
assertThat(holder.getGenericIndexedMap().keySet().iterator().next()).isEqualTo(1);
assertThat(holder.getGenericIndexedMap().values().iterator().next().get(0)).isEqualTo(Long.valueOf(10));
assertThat(holder.getGenericIndexedMap()).containsOnly(entry(1, List.of(10L)));
}
@Test
@@ -452,8 +448,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(holder);
bw.setPropertyValue("derivedIndexedMap[1]", inputValue);
assertThat(holder.getDerivedIndexedMap().keySet().iterator().next()).isEqualTo(1);
assertThat(holder.getDerivedIndexedMap().values().iterator().next().get(0)).isEqualTo(Long.valueOf(10));
assertThat(holder.getDerivedIndexedMap()).containsOnly(entry(1, List.of(10L)));
}
@Test
@@ -465,8 +460,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(holder);
bw.setPropertyValue("derivedIndexedMap[1]", inputValue);
assertThat(holder.getDerivedIndexedMap().keySet().iterator().next()).isEqualTo(1);
assertThat(holder.getDerivedIndexedMap().values().iterator().next().get(0)).isEqualTo(Long.valueOf(10));
assertThat(holder.getDerivedIndexedMap()).containsOnly(entry(1, List.of(10L)));
}
@Test
@@ -477,8 +471,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(holder);
bw.setPropertyValue("derivedIndexedMap[1]", inputValue);
assertThat(holder.getDerivedIndexedMap().keySet().iterator().next()).isEqualTo(1);
assertThat(holder.getDerivedIndexedMap().values().iterator().next().get(0)).isEqualTo(Long.valueOf(10));
assertThat(holder.getDerivedIndexedMap()).containsOnly(entry(1, List.of(10L)));
}
@Test
@@ -490,8 +483,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(holder);
bw.setPropertyValue("multiValueMap[1]", inputValue);
assertThat(holder.getMultiValueMap().keySet().iterator().next()).isEqualTo(1);
assertThat(holder.getMultiValueMap().values().iterator().next().get(0)).isEqualTo(Long.valueOf(10));
assertThat(holder.getMultiValueMap()).containsOnly(entry(1, List.of(10L)));
}
@Test
@@ -503,8 +495,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(holder);
bw.setPropertyValue("multiValueMap[1]", inputValue);
assertThat(holder.getMultiValueMap().keySet().iterator().next()).isEqualTo(1);
assertThat(holder.getMultiValueMap().values().iterator().next().get(0)).isEqualTo(Long.valueOf(10));
assertThat(holder.getMultiValueMap()).containsOnly(entry(1, List.of(10L)));
}
@Test
@@ -515,8 +506,7 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(holder);
bw.setPropertyValue("multiValueMap[1]", inputValue);
assertThat(holder.getMultiValueMap().keySet().iterator().next()).isEqualTo(1);
assertThat(holder.getMultiValueMap().values().iterator().next().get(0)).isEqualTo(Long.valueOf(10));
assertThat(holder.getMultiValueMap()).containsOnly(entry(1, List.of(10L)));
}
@Test
@@ -526,8 +516,8 @@ class BeanWrapperGenericsTests {
bw.setPropertyValue("genericProperty", "10");
bw.setPropertyValue("genericListProperty", new String[] {"20", "30"});
assertThat(gb.getGenericProperty()).isEqualTo(10);
assertThat(gb.getGenericListProperty().get(0)).isEqualTo(20);
assertThat(gb.getGenericListProperty().get(1)).isEqualTo(30);
assertThat(gb.getGenericListProperty()).element(0).isEqualTo(20);
assertThat(gb.getGenericListProperty()).element(1).isEqualTo(30);
}
@Test
@@ -536,9 +526,9 @@ class BeanWrapperGenericsTests {
BeanWrapper bw = new BeanWrapperImpl(gb);
bw.setPropertyValue("genericProperty", "10");
bw.setPropertyValue("genericListProperty", new String[] {"20", "30"});
assertThat(gb.getGenericProperty().iterator().next()).isEqualTo(10);
assertThat(gb.getGenericListProperty().get(0).iterator().next()).isEqualTo(20);
assertThat(gb.getGenericListProperty().get(1).iterator().next()).isEqualTo(30);
assertThat(gb.getGenericProperty()).element(0).isEqualTo(10);
assertThat(gb.getGenericListProperty().get(0)).element(0).isEqualTo(20);
assertThat(gb.getGenericListProperty().get(1)).element(0).isEqualTo(30);
}
@Test
@@ -16,7 +16,9 @@
package org.springframework.beans;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.junit.jupiter.api.Test;
@@ -30,10 +32,10 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* @author Chris Beams
* @author Juergen Hoeller
*/
public class MutablePropertyValuesTests extends AbstractPropertyValuesTests {
class MutablePropertyValuesTests {
@Test
public void testValid() {
void valid() {
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.addPropertyValue(new PropertyValue("forname", "Tony"));
pvs.addPropertyValue(new PropertyValue("surname", "Blair"));
@@ -48,7 +50,7 @@ public class MutablePropertyValuesTests extends AbstractPropertyValuesTests {
}
@Test
public void testAddOrOverride() {
void addOrOverride() {
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.addPropertyValue(new PropertyValue("forname", "Tony"));
pvs.addPropertyValue(new PropertyValue("surname", "Blair"));
@@ -56,25 +58,25 @@ public class MutablePropertyValuesTests extends AbstractPropertyValuesTests {
doTestTony(pvs);
PropertyValue addedPv = new PropertyValue("rod", "Rod");
pvs.addPropertyValue(addedPv);
assertThat(pvs.getPropertyValue("rod").equals(addedPv)).isTrue();
assertThat(pvs.getPropertyValue("rod")).isEqualTo(addedPv);
PropertyValue changedPv = new PropertyValue("forname", "Greg");
pvs.addPropertyValue(changedPv);
assertThat(pvs.getPropertyValue("forname").equals(changedPv)).isTrue();
assertThat(pvs.getPropertyValue("forname")).isEqualTo(changedPv);
}
@Test
public void testChangesOnEquals() {
void changesOnEquals() {
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.addPropertyValue(new PropertyValue("forname", "Tony"));
pvs.addPropertyValue(new PropertyValue("surname", "Blair"));
pvs.addPropertyValue(new PropertyValue("age", "50"));
MutablePropertyValues pvs2 = pvs;
PropertyValues changes = pvs2.changesSince(pvs);
assertThat(changes.getPropertyValues().length).as("changes are empty").isEqualTo(0);
assertThat(changes.getPropertyValues()).as("changes are empty").isEmpty();
}
@Test
public void testChangeOfOneField() {
void changeOfOneField() {
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.addPropertyValue(new PropertyValue("forname", "Tony"));
pvs.addPropertyValue(new PropertyValue("surname", "Blair"));
@@ -82,33 +84,31 @@ public class MutablePropertyValuesTests extends AbstractPropertyValuesTests {
MutablePropertyValues pvs2 = new MutablePropertyValues(pvs);
PropertyValues changes = pvs2.changesSince(pvs);
assertThat(changes.getPropertyValues().length).as("changes are empty, not of length " + changes.getPropertyValues().length)
.isEqualTo(0);
assertThat(changes.getPropertyValues()).as("changes").isEmpty();
pvs2.addPropertyValue(new PropertyValue("forname", "Gordon"));
changes = pvs2.changesSince(pvs);
assertThat(changes.getPropertyValues().length).as("1 change").isEqualTo(1);
assertThat(changes.getPropertyValues()).as("1 change").hasSize(1);
PropertyValue fn = changes.getPropertyValue("forname");
assertThat(fn).as("change is forname").isNotNull();
assertThat(fn.getValue().equals("Gordon")).as("new value is gordon").isTrue();
assertThat(fn.getValue()).isEqualTo("Gordon");
MutablePropertyValues pvs3 = new MutablePropertyValues(pvs);
changes = pvs3.changesSince(pvs);
assertThat(changes.getPropertyValues().length).as("changes are empty, not of length " + changes.getPropertyValues().length)
.isEqualTo(0);
assertThat(changes.getPropertyValues()).as("changes").isEmpty();
// add new
pvs3.addPropertyValue(new PropertyValue("foo", "bar"));
pvs3.addPropertyValue(new PropertyValue("fi", "fum"));
changes = pvs3.changesSince(pvs);
assertThat(changes.getPropertyValues().length).as("2 change").isEqualTo(2);
assertThat(changes.getPropertyValues()).as("2 changes").hasSize(2);
fn = changes.getPropertyValue("foo");
assertThat(fn).as("change in foo").isNotNull();
assertThat(fn.getValue().equals("bar")).as("new value is bar").isTrue();
assertThat(fn.getValue()).isEqualTo("bar");
}
@Test
public void iteratorContainsPropertyValue() {
void iteratorContainsPropertyValue() {
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("foo", "bar");
@@ -122,28 +122,55 @@ public class MutablePropertyValuesTests extends AbstractPropertyValuesTests {
}
@Test
public void iteratorIsEmptyForEmptyValues() {
void iteratorIsEmptyForEmptyValues() {
MutablePropertyValues pvs = new MutablePropertyValues();
Iterator<PropertyValue> it = pvs.iterator();
assertThat(it.hasNext()).isFalse();
}
@Test
public void streamContainsPropertyValue() {
void streamContainsPropertyValue() {
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("foo", "bar");
assertThat(pvs.stream()).isNotNull();
assertThat(pvs.stream().count()).isEqualTo(1L);
assertThat(pvs.stream().anyMatch(pv -> "foo".equals(pv.getName()) && "bar".equals(pv.getValue()))).isTrue();
assertThat(pvs.stream().anyMatch(pv -> "bar".equals(pv.getName()) && "foo".equals(pv.getValue()))).isFalse();
assertThat(pvs.stream()).hasSize(1);
assertThat(pvs.stream()).anyMatch(pv -> "foo".equals(pv.getName()) && "bar".equals(pv.getValue()));
assertThat(pvs.stream()).noneMatch(pv -> "bar".equals(pv.getName()) && "foo".equals(pv.getValue()));
}
@Test
public void streamIsEmptyForEmptyValues() {
void streamIsEmptyForEmptyValues() {
MutablePropertyValues pvs = new MutablePropertyValues();
assertThat(pvs.stream()).isNotNull();
assertThat(pvs.stream().count()).isEqualTo(0L);
assertThat(pvs.stream()).isEmpty();
}
/**
* Must contain: forname=Tony surname=Blair age=50
*/
protected void doTestTony(PropertyValues pvs) {
PropertyValue[] propertyValues = pvs.getPropertyValues();
assertThat(propertyValues).hasSize(3);
assertThat(pvs.contains("forname")).as("Contains forname").isTrue();
assertThat(pvs.contains("surname")).as("Contains surname").isTrue();
assertThat(pvs.contains("age")).as("Contains age").isTrue();
assertThat(pvs.contains("tory")).as("Doesn't contain tory").isFalse();
Map<String, String> map = new HashMap<>();
map.put("forname", "Tony");
map.put("surname", "Blair");
map.put("age", "50");
for (PropertyValue element : propertyValues) {
Object val = map.get(element.getName());
assertThat(val).as("Can't have unexpected value").isNotNull();
assertThat(val).isInstanceOf(String.class);
assertThat(val).isEqualTo(element.getValue());
map.remove(element.getName());
}
assertThat(map).isEmpty();
}
}
@@ -112,7 +112,7 @@ public class BeanFactoryUtilsTests {
List<String> names = Arrays.asList(
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.listableBeanFactory, IndexedTestBean.class));
assertThat(names).hasSize(1);
assertThat(names.contains("indexedBean")).isTrue();
assertThat(names).contains("indexedBean");
// Distinguish from default ListableBeanFactory behavior
assertThat(listableBeanFactory.getBeanNamesForType(IndexedTestBean.class)).isEmpty();
}
@@ -123,10 +123,10 @@ public class BeanFactoryUtilsTests {
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.listableBeanFactory, ITestBean.class));
// includes 2 TestBeans from FactoryBeans (DummyFactory definitions)
assertThat(names).hasSize(4);
assertThat(names.contains("test")).isTrue();
assertThat(names.contains("test3")).isTrue();
assertThat(names.contains("testFactory1")).isTrue();
assertThat(names.contains("testFactory2")).isTrue();
assertThat(names).contains("test");
assertThat(names).contains("test3");
assertThat(names).contains("testFactory1");
assertThat(names).contains("testFactory2");
}
@Test
@@ -134,7 +134,7 @@ public class BeanFactoryUtilsTests {
StaticListableBeanFactory lbf = new StaticListableBeanFactory();
lbf.addBean("foo", new Object());
Map<String, ?> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, ITestBean.class, true, false);
assertThat(beans.isEmpty()).isTrue();
assertThat(beans).isEmpty();
}
@Test
@@ -155,7 +155,7 @@ public class BeanFactoryUtilsTests {
assertThat(beans.get("t1")).isEqualTo(t1);
assertThat(beans.get("t2")).isEqualTo(t2);
assertThat(beans.get("t3")).isEqualTo(t3.getObject());
assertThat(beans.get("t4") instanceof TestBean).isTrue();
assertThat(beans.get("t4")).isInstanceOf(TestBean.class);
beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, DummyFactory.class, true, true);
assertThat(beans).hasSize(2);
@@ -191,7 +191,7 @@ public class BeanFactoryUtilsTests {
assertThat(beans.get("t1")).isEqualTo(t1);
assertThat(beans.get("t2")).isEqualTo(t2);
assertThat(beans.get("t3")).isEqualTo(t3.getObject());
assertThat(beans.get("t4") instanceof TestBean).isTrue();
assertThat(beans.get("t4")).isInstanceOf(TestBean.class);
// t3 and t4 are found here as of Spring 2.0, since they are pre-registered
// singleton instances, while testFactory1 and testFactory are *not* found
// because they are FactoryBean definitions that haven't been initialized yet.
@@ -210,11 +210,11 @@ public class BeanFactoryUtilsTests {
assertThat(beans.get("test3")).isEqualTo(test3);
assertThat(beans.get("test")).isEqualTo(test);
assertThat(beans.get("testFactory1")).isEqualTo(testFactory1);
assertThat(beans.get("testFactory2") instanceof TestBean).isTrue();
assertThat(beans.get("testFactory2")).isInstanceOf(TestBean.class);
assertThat(beans.get("t1")).isEqualTo(t1);
assertThat(beans.get("t2")).isEqualTo(t2);
assertThat(beans.get("t3")).isEqualTo(t3.getObject());
assertThat(beans.get("t4") instanceof TestBean).isTrue();
assertThat(beans.get("t4")).isInstanceOf(TestBean.class);
beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.listableBeanFactory, DummyFactory.class, true, true);
assertThat(beans).hasSize(4);
@@ -257,7 +257,7 @@ public class BeanFactoryUtilsTests {
assertThat(beans.get("test3")).isEqualTo(test3);
assertThat(beans.get("test")).isEqualTo(test);
assertThat(beans.get("testFactory1")).isEqualTo(testFactory1);
assertThat(beans.get("testFactory2") instanceof TestBean).isTrue();
assertThat(beans.get("testFactory2")).isInstanceOf(TestBean.class);
beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.listableBeanFactory, DummyFactory.class, true, true);
assertThat(beans).hasSize(2);
@@ -282,7 +282,7 @@ public class BeanFactoryUtilsTests {
List<String> names = Arrays.asList(
BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.listableBeanFactory, TestAnnotation.class));
assertThat(names).hasSize(1);
assertThat(names.contains("annotatedBean")).isTrue();
assertThat(names).contains("annotatedBean");
// Distinguish from default ListableBeanFactory behavior
assertThat(listableBeanFactory.getBeanNamesForAnnotation(TestAnnotation.class)).isEmpty();
}
@@ -294,8 +294,8 @@ public class BeanFactoryUtilsTests {
List<String> names = Arrays.asList(
BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.listableBeanFactory, TestAnnotation.class));
assertThat(names).hasSize(2);
assertThat(names.contains("annotatedBean")).isTrue();
assertThat(names.contains("anotherAnnotatedBean")).isTrue();
assertThat(names).contains("annotatedBean");
assertThat(names).contains("anotherAnnotatedBean");
}
@Test
@@ -79,9 +79,11 @@ import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.beans.testfixture.beans.factory.DummyFactory;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.Order;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.io.Resource;
@@ -1484,6 +1486,55 @@ class DefaultListableBeanFactoryTests {
assertThat(bean.getSpouse2()).isNull();
}
@Test
void orderFromAttribute() {
GenericBeanDefinition bd1 = new GenericBeanDefinition();
bd1.setBeanClass(TestBean.class);
bd1.setPropertyValues(new MutablePropertyValues(List.of(new PropertyValue("name", "lowest"))));
bd1.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.LOWEST_PRECEDENCE);
lbf.registerBeanDefinition("bean1", bd1);
GenericBeanDefinition bd2 = new GenericBeanDefinition();
bd2.setBeanClass(TestBean.class);
bd2.setPropertyValues(new MutablePropertyValues(List.of(new PropertyValue("name", "highest"))));
bd2.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.HIGHEST_PRECEDENCE);
lbf.registerBeanDefinition("bean2", bd2);
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream().map(TestBean::getName))
.containsExactly("highest", "lowest");
}
@Test
void orderFromAttributeOverrideAnnotation() {
lbf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
RootBeanDefinition rbd1 = new RootBeanDefinition(LowestPrecedenceTestBeanFactoryBean.class);
rbd1.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.HIGHEST_PRECEDENCE);
lbf.registerBeanDefinition("lowestPrecedenceFactory", rbd1);
RootBeanDefinition rbd2 = new RootBeanDefinition(HighestPrecedenceTestBeanFactoryBean.class);
rbd2.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Ordered.LOWEST_PRECEDENCE);
lbf.registerBeanDefinition("highestPrecedenceFactory", rbd2);
GenericBeanDefinition bd1 = new GenericBeanDefinition();
bd1.setFactoryBeanName("highestPrecedenceFactory");
lbf.registerBeanDefinition("bean1", bd1);
GenericBeanDefinition bd2 = new GenericBeanDefinition();
bd2.setFactoryBeanName("lowestPrecedenceFactory");
lbf.registerBeanDefinition("bean2", bd2);
assertThat(lbf.getBeanProvider(TestBean.class).orderedStream().map(TestBean::getName))
.containsExactly("fromLowestPrecedenceTestBeanFactoryBean", "fromHighestPrecedenceTestBeanFactoryBean");
}
@Test
void invalidOrderAttribute() {
GenericBeanDefinition bd1 = new GenericBeanDefinition();
bd1.setBeanClass(TestBean.class);
bd1.setAttribute(AbstractBeanDefinition.ORDER_ATTRIBUTE, Boolean.TRUE);
lbf.registerBeanDefinition("bean1", bd1);
GenericBeanDefinition bd2 = new GenericBeanDefinition();
bd2.setBeanClass(TestBean.class);
lbf.registerBeanDefinition("bean", bd2);
assertThatIllegalStateException()
.isThrownBy(() -> lbf.getBeanProvider(TestBean.class).orderedStream().collect(Collectors.toList()))
.withMessageContaining("Invalid value type for attribute");
}
@Test
void dependsOnCycle() {
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
@@ -1794,19 +1845,19 @@ class DefaultListableBeanFactoryTests {
resolved.add(instance);
}
assertThat(resolved).hasSize(2);
assertThat(resolved.contains(lbf.getBean("bd1"))).isTrue();
assertThat(resolved.contains(lbf.getBean("bd2"))).isTrue();
assertThat(resolved).contains(lbf.getBean("bd1"));
assertThat(resolved).contains(lbf.getBean("bd2"));
resolved = new HashSet<>();
provider.forEach(resolved::add);
assertThat(resolved).hasSize(2);
assertThat(resolved.contains(lbf.getBean("bd1"))).isTrue();
assertThat(resolved.contains(lbf.getBean("bd2"))).isTrue();
assertThat(resolved).contains(lbf.getBean("bd1"));
assertThat(resolved).contains(lbf.getBean("bd2"));
resolved = provider.stream().collect(Collectors.toSet());
assertThat(resolved).hasSize(2);
assertThat(resolved.contains(lbf.getBean("bd1"))).isTrue();
assertThat(resolved.contains(lbf.getBean("bd2"))).isTrue();
assertThat(resolved).contains(lbf.getBean("bd1"));
assertThat(resolved).contains(lbf.getBean("bd2"));
}
@Test
@@ -1843,19 +1894,19 @@ class DefaultListableBeanFactoryTests {
resolved.add(instance);
}
assertThat(resolved).hasSize(2);
assertThat(resolved.contains(lbf.getBean("bd1"))).isTrue();
assertThat(resolved.contains(lbf.getBean("bd2"))).isTrue();
assertThat(resolved).contains(lbf.getBean("bd1"));
assertThat(resolved).contains(lbf.getBean("bd2"));
resolved = new HashSet<>();
provider.forEach(resolved::add);
assertThat(resolved).hasSize(2);
assertThat(resolved.contains(lbf.getBean("bd1"))).isTrue();
assertThat(resolved.contains(lbf.getBean("bd2"))).isTrue();
assertThat(resolved).contains(lbf.getBean("bd1"));
assertThat(resolved).contains(lbf.getBean("bd2"));
resolved = provider.stream().collect(Collectors.toSet());
assertThat(resolved).hasSize(2);
assertThat(resolved.contains(lbf.getBean("bd1"))).isTrue();
assertThat(resolved.contains(lbf.getBean("bd2"))).isTrue();
assertThat(resolved).contains(lbf.getBean("bd1"));
assertThat(resolved).contains(lbf.getBean("bd2"));
}
@Test
@@ -2187,9 +2238,9 @@ class DefaultListableBeanFactoryTests {
ObjectProvider<TestBean> testBeanProvider = lbf.getBeanProvider(ResolvableType.forClass(TestBean.class));
List<TestBean> resolved = testBeanProvider.orderedStream().toList();
assertThat(resolved.size()).isEqualTo(3);
assertThat(resolved.get(0)).isSameAs(lbf.getBean("highPriorityTestBean"));
assertThat(resolved.get(1)).isSameAs(lbf.getBean("lowPriorityTestBean"));
assertThat(resolved.get(2)).isSameAs(lbf.getBean("plainTestBean"));
assertThat(resolved).element(0).isSameAs(lbf.getBean("highPriorityTestBean"));
assertThat(resolved).element(1).isSameAs(lbf.getBean("lowPriorityTestBean"));
assertThat(resolved).element(2).isSameAs(lbf.getBean("plainTestBean"));
}
@Test
@@ -3365,7 +3416,7 @@ class DefaultListableBeanFactoryTests {
}
private static class PriorityTestBeanFactory {
static class PriorityTestBeanFactory {
public static LowPriorityTestBean lowPriorityTestBean() {
return new LowPriorityTestBean();
@@ -3426,4 +3477,34 @@ class DefaultListableBeanFactoryTests {
}
}
@Order
private static class LowestPrecedenceTestBeanFactoryBean implements FactoryBean<TestBean> {
@Override
public TestBean getObject() {
return new TestBean("fromLowestPrecedenceTestBeanFactoryBean");
}
@Override
public Class<?> getObjectType() {
return TestBean.class;
}
}
@Order(Ordered.HIGHEST_PRECEDENCE)
private static class HighestPrecedenceTestBeanFactoryBean implements FactoryBean<TestBean> {
@Override
public TestBean getObject() {
return new TestBean("fromHighestPrecedenceTestBeanFactoryBean");
}
@Override
public Class<?> getObjectType() {
return TestBean.class;
}
}
}
@@ -569,14 +569,14 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
assertThat(bean.getNestedTestBeans()).hasSize(2);
assertThat(bean.getNestedTestBeans().get(0)).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans().get(1)).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans()).element(0).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()).element(1).isSameAs(ntb2);
assertThat(bean.nestedTestBeansSetter).hasSize(2);
assertThat(bean.nestedTestBeansSetter.get(0)).isSameAs(ntb1);
assertThat(bean.nestedTestBeansSetter.get(1)).isSameAs(ntb2);
assertThat(bean.nestedTestBeansSetter).element(0).isSameAs(ntb1);
assertThat(bean.nestedTestBeansSetter).element(1).isSameAs(ntb2);
assertThat(bean.nestedTestBeansField).hasSize(2);
assertThat(bean.nestedTestBeansField.get(0)).isSameAs(ntb1);
assertThat(bean.nestedTestBeansField.get(1)).isSameAs(ntb2);
assertThat(bean.nestedTestBeansField).element(0).isSameAs(ntb1);
assertThat(bean.nestedTestBeansField).element(1).isSameAs(ntb2);
}
@Test
@@ -600,11 +600,11 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
assertThat(bean.getNestedTestBeans()).hasSize(1);
assertThat(bean.getNestedTestBeans().get(0)).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()).element(0).isSameAs(ntb1);
assertThat(bean.nestedTestBeansSetter).hasSize(1);
assertThat(bean.nestedTestBeansSetter.get(0)).isSameAs(ntb1);
assertThat(bean.nestedTestBeansSetter).element(0).isSameAs(ntb1);
assertThat(bean.nestedTestBeansField).hasSize(1);
assertThat(bean.nestedTestBeansField.get(0)).isSameAs(ntb1);
assertThat(bean.nestedTestBeansField).element(0).isSameAs(ntb1);
}
@Test
@@ -712,14 +712,14 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
assertThat(bean.getNestedTestBeans()).hasSize(2);
assertThat(bean.getNestedTestBeans().get(0)).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans().get(1)).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()).element(0).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans()).element(1).isSameAs(ntb1);
assertThat(bean.nestedTestBeansSetter).hasSize(2);
assertThat(bean.nestedTestBeansSetter.get(0)).isSameAs(ntb2);
assertThat(bean.nestedTestBeansSetter.get(1)).isSameAs(ntb1);
assertThat(bean.nestedTestBeansSetter).element(0).isSameAs(ntb2);
assertThat(bean.nestedTestBeansSetter).element(1).isSameAs(ntb1);
assertThat(bean.nestedTestBeansField).hasSize(2);
assertThat(bean.nestedTestBeansField.get(0)).isSameAs(ntb2);
assertThat(bean.nestedTestBeansField.get(1)).isSameAs(ntb1);
assertThat(bean.nestedTestBeansField).element(0).isSameAs(ntb2);
assertThat(bean.nestedTestBeansField).element(1).isSameAs(ntb1);
}
@Test
@@ -745,14 +745,14 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
assertThat(bean.getNestedTestBeans()).hasSize(2);
assertThat(bean.getNestedTestBeans().get(0)).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans().get(1)).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()).element(0).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans()).element(1).isSameAs(ntb1);
assertThat(bean.nestedTestBeansSetter).hasSize(2);
assertThat(bean.nestedTestBeansSetter.get(0)).isSameAs(ntb2);
assertThat(bean.nestedTestBeansSetter.get(1)).isSameAs(ntb1);
assertThat(bean.nestedTestBeansSetter).element(0).isSameAs(ntb2);
assertThat(bean.nestedTestBeansSetter).element(1).isSameAs(ntb1);
assertThat(bean.nestedTestBeansField).hasSize(2);
assertThat(bean.nestedTestBeansField.get(0)).isSameAs(ntb2);
assertThat(bean.nestedTestBeansField.get(1)).isSameAs(ntb1);
assertThat(bean.nestedTestBeansField).element(0).isSameAs(ntb2);
assertThat(bean.nestedTestBeansField).element(1).isSameAs(ntb1);
}
@Test
@@ -1055,7 +1055,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getTestBean3()).isNull();
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBeans()).hasSize(1);
assertThat(bean.getNestedTestBeans().get(0)).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans()).element(0).isSameAs(ntb2);
Map<String, NestedTestBean> map = bf.getBeansOfType(NestedTestBean.class);
assertThat(map.get("nestedTestBean1")).isNull();
@@ -1077,8 +1077,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getTestBean3()).isNull();
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBeans()).hasSize(2);
assertThat(bean.getNestedTestBeans().get(0)).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans().get(1)).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans()).element(0).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()).element(1).isSameAs(ntb2);
}
@Test
@@ -1113,8 +1113,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getTestBean3()).isNull();
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBeans()).hasSize(2);
assertThat(bean.getNestedTestBeans().get(0)).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans().get(1)).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()).element(0).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans()).element(1).isSameAs(ntb1);
}
@Test
@@ -1130,8 +1130,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
SingleConstructorVarargBean bean = bf.getBean("annotatedBean", SingleConstructorVarargBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getNestedTestBeans()).hasSize(2);
assertThat(bean.getNestedTestBeans().get(0)).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans().get(1)).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()).element(0).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans()).element(1).isSameAs(ntb1);
}
@Test
@@ -1143,7 +1143,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
SingleConstructorVarargBean bean = bf.getBean("annotatedBean", SingleConstructorVarargBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getNestedTestBeans()).isNotNull();
assertThat(bean.getNestedTestBeans().isEmpty()).isTrue();
assertThat(bean.getNestedTestBeans()).isEmpty();
}
@Test
@@ -1159,8 +1159,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
SingleConstructorRequiredCollectionBean bean = bf.getBean("annotatedBean", SingleConstructorRequiredCollectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getNestedTestBeans()).hasSize(2);
assertThat(bean.getNestedTestBeans().get(0)).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans().get(1)).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()).element(0).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans()).element(1).isSameAs(ntb1);
}
@Test
@@ -1172,7 +1172,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
SingleConstructorRequiredCollectionBean bean = bf.getBean("annotatedBean", SingleConstructorRequiredCollectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getNestedTestBeans()).isNotNull();
assertThat(bean.getNestedTestBeans().isEmpty()).isTrue();
assertThat(bean.getNestedTestBeans()).isEmpty();
}
@Test
@@ -1188,8 +1188,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
SingleConstructorOptionalCollectionBean bean = bf.getBean("annotatedBean", SingleConstructorOptionalCollectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getNestedTestBeans()).hasSize(2);
assertThat(bean.getNestedTestBeans().get(0)).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans().get(1)).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()).element(0).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans()).element(1).isSameAs(ntb1);
}
@Test
@@ -1274,17 +1274,17 @@ public class AutowiredAnnotationBeanPostProcessorTests {
MapFieldInjectionBean bean = bf.getBean("annotatedBean", MapFieldInjectionBean.class);
assertThat(bean.getTestBeanMap()).hasSize(2);
assertThat(bean.getTestBeanMap().keySet().contains("testBean1")).isTrue();
assertThat(bean.getTestBeanMap().keySet().contains("testBean2")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb1)).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb2)).isTrue();
assertThat(bean.getTestBeanMap()).containsKey("testBean1");
assertThat(bean.getTestBeanMap()).containsKey("testBean2");
assertThat(bean.getTestBeanMap()).containsValue(tb1);
assertThat(bean.getTestBeanMap()).containsValue(tb2);
bean = bf.getBean("annotatedBean", MapFieldInjectionBean.class);
assertThat(bean.getTestBeanMap()).hasSize(2);
assertThat(bean.getTestBeanMap().keySet().contains("testBean1")).isTrue();
assertThat(bean.getTestBeanMap().keySet().contains("testBean2")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb1)).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb2)).isTrue();
assertThat(bean.getTestBeanMap()).containsKey("testBean1");
assertThat(bean.getTestBeanMap()).containsKey("testBean2");
assertThat(bean.getTestBeanMap()).containsValue(tb1);
assertThat(bean.getTestBeanMap()).containsValue(tb2);
}
@Test
@@ -1297,14 +1297,14 @@ public class AutowiredAnnotationBeanPostProcessorTests {
MapMethodInjectionBean bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
assertThat(bean.getTestBeanMap()).hasSize(1);
assertThat(bean.getTestBeanMap().keySet().contains("testBean")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb)).isTrue();
assertThat(bean.getTestBeanMap()).containsKey("testBean");
assertThat(bean.getTestBeanMap()).containsValue(tb);
assertThat(bean.getTestBean()).isSameAs(tb);
bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
assertThat(bean.getTestBeanMap()).hasSize(1);
assertThat(bean.getTestBeanMap().keySet().contains("testBean")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb)).isTrue();
assertThat(bean.getTestBeanMap()).containsKey("testBean");
assertThat(bean.getTestBeanMap()).containsValue(tb);
assertThat(bean.getTestBean()).isSameAs(tb);
}
@@ -1329,8 +1329,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
MapMethodInjectionBean bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
TestBean tb = bf.getBean("testBean1", TestBean.class);
assertThat(bean.getTestBeanMap()).hasSize(1);
assertThat(bean.getTestBeanMap().keySet().contains("testBean1")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb)).isTrue();
assertThat(bean.getTestBeanMap()).containsKey("testBean1");
assertThat(bean.getTestBeanMap()).containsValue(tb);
assertThat(bean.getTestBean()).isSameAs(tb);
}
@@ -1501,7 +1501,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
SelfInjectionBean bean2 = bf.getBean("annotatedBean2", SelfInjectionBean.class);
assertThat(bean.reference).isSameAs(bean2);
assertThat(bean.referenceCollection).hasSize(1);
assertThat(bean.referenceCollection.get(0)).isSameAs(bean2);
assertThat(bean.referenceCollection).element(0).isSameAs(bean2);
}
@Test
@@ -1522,7 +1522,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
SelfInjectionCollectionBean bean2 = bf.getBean("annotatedBean2", SelfInjectionCollectionBean.class);
assertThat(bean.reference).isSameAs(bean2);
assertThat(bean2.referenceCollection).hasSize(1);
assertThat(bean.referenceCollection.get(0)).isSameAs(bean2);
assertThat(bean.referenceCollection).element(0).isSameAs(bean2);
}
@Test
@@ -1610,18 +1610,18 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getUniqueTestBeanWithDefault()).isEqualTo(bf.getBean("testBean"));
assertThat(bean.consumeUniqueTestBean()).isEqualTo(bf.getBean("testBean"));
List<?> testBeans = bean.iterateTestBeans();
List<TestBean> testBeans = bean.iterateTestBeans();
assertThat(testBeans).hasSize(1);
assertThat(testBeans.contains(bf.getBean("testBean"))).isTrue();
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
testBeans = bean.forEachTestBeans();
assertThat(testBeans).hasSize(1);
assertThat(testBeans.contains(bf.getBean("testBean"))).isTrue();
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
testBeans = bean.streamTestBeans();
assertThat(testBeans).hasSize(1);
assertThat(testBeans.contains(bf.getBean("testBean"))).isTrue();
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
testBeans = bean.sortedTestBeans();
assertThat(testBeans).hasSize(1);
assertThat(testBeans.contains(bf.getBean("testBean"))).isTrue();
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
}
@Test
@@ -1638,18 +1638,18 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getUniqueTestBeanWithDefault()).isSameAs(bf.getBean("testBean"));
assertThat(bean.consumeUniqueTestBean()).isEqualTo(bf.getBean("testBean"));
List<?> testBeans = bean.iterateTestBeans();
List<TestBean> testBeans = bean.iterateTestBeans();
assertThat(testBeans).hasSize(1);
assertThat(testBeans.contains(bf.getBean("testBean"))).isTrue();
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
testBeans = bean.forEachTestBeans();
assertThat(testBeans).hasSize(1);
assertThat(testBeans.contains(bf.getBean("testBean"))).isTrue();
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
testBeans = bean.streamTestBeans();
assertThat(testBeans).hasSize(1);
assertThat(testBeans.contains(bf.getBean("testBean"))).isTrue();
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
testBeans = bean.sortedTestBeans();
assertThat(testBeans).hasSize(1);
assertThat(testBeans.contains(bf.getBean("testBean"))).isTrue();
assertThat(testBeans).contains(bf.getBean("testBean", TestBean.class));
}
@Test
@@ -1666,13 +1666,13 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.consumeUniqueTestBean()).isNull();
List<?> testBeans = bean.iterateTestBeans();
assertThat(testBeans.isEmpty()).isTrue();
assertThat(testBeans).isEmpty();
testBeans = bean.forEachTestBeans();
assertThat(testBeans.isEmpty()).isTrue();
assertThat(testBeans).isEmpty();
testBeans = bean.streamTestBeans();
assertThat(testBeans.isEmpty()).isTrue();
assertThat(testBeans).isEmpty();
testBeans = bean.sortedTestBeans();
assertThat(testBeans.isEmpty()).isTrue();
assertThat(testBeans).isEmpty();
}
@Test
@@ -1690,20 +1690,20 @@ public class AutowiredAnnotationBeanPostProcessorTests {
List<?> testBeans = bean.iterateTestBeans();
assertThat(testBeans).hasSize(2);
assertThat(testBeans.get(0)).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans.get(1)).isSameAs(bf.getBean("testBean2"));
assertThat(testBeans).element(0).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans).element(1).isSameAs(bf.getBean("testBean2"));
testBeans = bean.forEachTestBeans();
assertThat(testBeans).hasSize(2);
assertThat(testBeans.get(0)).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans.get(1)).isSameAs(bf.getBean("testBean2"));
assertThat(testBeans).element(0).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans).element(1).isSameAs(bf.getBean("testBean2"));
testBeans = bean.streamTestBeans();
assertThat(testBeans).hasSize(2);
assertThat(testBeans.get(0)).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans.get(1)).isSameAs(bf.getBean("testBean2"));
assertThat(testBeans).element(0).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans).element(1).isSameAs(bf.getBean("testBean2"));
testBeans = bean.sortedTestBeans();
assertThat(testBeans).hasSize(2);
assertThat(testBeans.get(0)).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans.get(1)).isSameAs(bf.getBean("testBean2"));
assertThat(testBeans).element(0).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans).element(1).isSameAs(bf.getBean("testBean2"));
}
@Test
@@ -1728,20 +1728,20 @@ public class AutowiredAnnotationBeanPostProcessorTests {
List<?> testBeans = bean.iterateTestBeans();
assertThat(testBeans).hasSize(2);
assertThat(testBeans.get(0)).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans.get(1)).isSameAs(bf.getBean("testBean2"));
assertThat(testBeans).element(0).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans).element(1).isSameAs(bf.getBean("testBean2"));
testBeans = bean.forEachTestBeans();
assertThat(testBeans).hasSize(2);
assertThat(testBeans.get(0)).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans.get(1)).isSameAs(bf.getBean("testBean2"));
assertThat(testBeans).element(0).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans).element(1).isSameAs(bf.getBean("testBean2"));
testBeans = bean.streamTestBeans();
assertThat(testBeans).hasSize(2);
assertThat(testBeans.get(0)).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans.get(1)).isSameAs(bf.getBean("testBean2"));
assertThat(testBeans).element(0).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans).element(1).isSameAs(bf.getBean("testBean2"));
testBeans = bean.sortedTestBeans();
assertThat(testBeans).hasSize(2);
assertThat(testBeans.get(0)).isSameAs(bf.getBean("testBean2"));
assertThat(testBeans.get(1)).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans).element(0).isSameAs(bf.getBean("testBean2"));
assertThat(testBeans).element(1).isSameAs(bf.getBean("testBean1"));
}
@Test
@@ -1759,8 +1759,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
List<?> testBeans = bean.sortedTestBeans();
assertThat(testBeans).hasSize(2);
assertThat(testBeans.get(0)).isSameAs(bf.getBean("testBean2"));
assertThat(testBeans.get(1)).isSameAs(bf.getBean("testBean1"));
assertThat(testBeans).element(0).isSameAs(bf.getBean("testBean2"));
assertThat(testBeans).element(1).isSameAs(bf.getBean("testBean1"));
}
@Test
@@ -1988,8 +1988,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerArray[0]).isSameAs(iv);
assertThat(bean.stringList).hasSize(1);
assertThat(bean.integerList).hasSize(1);
assertThat(bean.stringList.get(0)).isSameAs(sv);
assertThat(bean.integerList.get(0)).isSameAs(iv);
assertThat(bean.stringList).element(0).isSameAs(sv);
assertThat(bean.integerList).element(0).isSameAs(iv);
assertThat(bean.stringMap).hasSize(1);
assertThat(bean.integerMap).hasSize(1);
assertThat(bean.stringMap.get("stringValue")).isSameAs(sv);
@@ -2002,8 +2002,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerRepositoryArray[0]).isSameAs(ir);
assertThat(bean.stringRepositoryList).hasSize(1);
assertThat(bean.integerRepositoryList).hasSize(1);
assertThat(bean.stringRepositoryList.get(0)).isSameAs(sr);
assertThat(bean.integerRepositoryList.get(0)).isSameAs(ir);
assertThat(bean.stringRepositoryList).element(0).isSameAs(sr);
assertThat(bean.integerRepositoryList).element(0).isSameAs(ir);
assertThat(bean.stringRepositoryMap).hasSize(1);
assertThat(bean.integerRepositoryMap).hasSize(1);
assertThat(bean.stringRepositoryMap.get("stringRepo")).isSameAs(sr);
@@ -2034,8 +2034,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerArray[0]).isSameAs(iv);
assertThat(bean.stringList).hasSize(1);
assertThat(bean.integerList).hasSize(1);
assertThat(bean.stringList.get(0)).isSameAs(sv);
assertThat(bean.integerList.get(0)).isSameAs(iv);
assertThat(bean.stringList).element(0).isSameAs(sv);
assertThat(bean.integerList).element(0).isSameAs(iv);
assertThat(bean.stringMap).hasSize(1);
assertThat(bean.integerMap).hasSize(1);
assertThat(bean.stringMap.get("stringValue")).isSameAs(sv);
@@ -2048,8 +2048,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerRepositoryArray[0]).isSameAs(ir);
assertThat(bean.stringRepositoryList).hasSize(1);
assertThat(bean.integerRepositoryList).hasSize(1);
assertThat(bean.stringRepositoryList.get(0)).isSameAs(sr);
assertThat(bean.integerRepositoryList.get(0)).isSameAs(ir);
assertThat(bean.stringRepositoryList).element(0).isSameAs(sr);
assertThat(bean.integerRepositoryList).element(0).isSameAs(ir);
assertThat(bean.stringRepositoryMap).hasSize(1);
assertThat(bean.integerRepositoryMap).hasSize(1);
assertThat(bean.stringRepositoryMap.get("stringRepo")).isSameAs(sr);
@@ -2076,8 +2076,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerRepositoryArray[0]).isSameAs(ir);
assertThat(bean.stringRepositoryList).hasSize(1);
assertThat(bean.integerRepositoryList).hasSize(1);
assertThat(bean.stringRepositoryList.get(0)).isSameAs(sr);
assertThat(bean.integerRepositoryList.get(0)).isSameAs(ir);
assertThat(bean.stringRepositoryList).element(0).isSameAs(sr);
assertThat(bean.integerRepositoryList).element(0).isSameAs(ir);
assertThat(bean.stringRepositoryMap).hasSize(1);
assertThat(bean.integerRepositoryMap).hasSize(1);
assertThat(bean.stringRepositoryMap.get("stringRepo")).isSameAs(sr);
@@ -2116,8 +2116,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerRepositoryArray[0]).isSameAs(ir);
assertThat(bean.stringRepositoryList).hasSize(1);
assertThat(bean.integerRepositoryList).hasSize(1);
assertThat(bean.stringRepositoryList.get(0)).isSameAs(sr);
assertThat(bean.integerRepositoryList.get(0)).isSameAs(ir);
assertThat(bean.stringRepositoryList).element(0).isSameAs(sr);
assertThat(bean.integerRepositoryList).element(0).isSameAs(ir);
assertThat(bean.stringRepositoryMap).hasSize(1);
assertThat(bean.integerRepositoryMap).hasSize(1);
assertThat(bean.stringRepositoryMap.get("stringRepo")).isSameAs(sr);
@@ -2143,8 +2143,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.stringRepositoryArray[0]).isSameAs(repo);
assertThat(bean.repositoryList).hasSize(1);
assertThat(bean.stringRepositoryList).hasSize(1);
assertThat(bean.repositoryList.get(0)).isSameAs(repo);
assertThat(bean.stringRepositoryList.get(0)).isSameAs(repo);
assertThat(bean.repositoryList).element(0).isSameAs(repo);
assertThat(bean.stringRepositoryList).element(0).isSameAs(repo);
assertThat(bean.repositoryMap).hasSize(1);
assertThat(bean.stringRepositoryMap).hasSize(1);
assertThat(bean.repositoryMap.get("repo")).isSameAs(repo);
@@ -2201,8 +2201,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.stringRepositoryArray[0]).isSameAs(repo);
assertThat(bean.repositoryList).hasSize(1);
assertThat(bean.stringRepositoryList).hasSize(1);
assertThat(bean.repositoryList.get(0)).isSameAs(repo);
assertThat(bean.stringRepositoryList.get(0)).isSameAs(repo);
assertThat(bean.repositoryList).element(0).isSameAs(repo);
assertThat(bean.stringRepositoryList).element(0).isSameAs(repo);
assertThat(bean.repositoryMap).hasSize(1);
assertThat(bean.stringRepositoryMap).hasSize(1);
assertThat(bean.repositoryMap.get("repo")).isSameAs(repo);
@@ -2232,8 +2232,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.stringRepositoryArray[0]).isSameAs(repo);
assertThat(bean.repositoryList).hasSize(1);
assertThat(bean.stringRepositoryList).hasSize(1);
assertThat(bean.repositoryList.get(0)).isSameAs(repo);
assertThat(bean.stringRepositoryList.get(0)).isSameAs(repo);
assertThat(bean.repositoryList).element(0).isSameAs(repo);
assertThat(bean.stringRepositoryList).element(0).isSameAs(repo);
assertThat(bean.repositoryMap).hasSize(1);
assertThat(bean.stringRepositoryMap).hasSize(1);
assertThat(bean.repositoryMap.get("repo")).isSameAs(repo);
@@ -2272,8 +2272,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerArray[0]).isSameAs(iv);
assertThat(bean.stringList).hasSize(1);
assertThat(bean.integerList).hasSize(1);
assertThat(bean.stringList.get(0)).isSameAs(sv);
assertThat(bean.integerList.get(0)).isSameAs(iv);
assertThat(bean.stringList).element(0).isSameAs(sv);
assertThat(bean.integerList).element(0).isSameAs(iv);
assertThat(bean.stringMap).hasSize(1);
assertThat(bean.integerMap).hasSize(1);
assertThat(bean.stringMap.get("stringValue")).isSameAs(sv);
@@ -2286,8 +2286,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerRepositoryArray[0]).isSameAs(ir);
assertThat(bean.stringRepositoryList).hasSize(1);
assertThat(bean.integerRepositoryList).hasSize(1);
assertThat(bean.stringRepositoryList.get(0)).isSameAs(sr);
assertThat(bean.integerRepositoryList.get(0)).isSameAs(ir);
assertThat(bean.stringRepositoryList).element(0).isSameAs(sr);
assertThat(bean.integerRepositoryList).element(0).isSameAs(ir);
assertThat(bean.stringRepositoryMap).hasSize(1);
assertThat(bean.integerRepositoryMap).hasSize(1);
assertThat(bean.stringRepositoryMap.get("stringRepo")).isSameAs(sr);
@@ -2318,8 +2318,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerArray[0]).isSameAs(iv);
assertThat(bean.stringList).hasSize(1);
assertThat(bean.integerList).hasSize(1);
assertThat(bean.stringList.get(0)).isSameAs(sv);
assertThat(bean.integerList.get(0)).isSameAs(iv);
assertThat(bean.stringList).element(0).isSameAs(sv);
assertThat(bean.integerList).element(0).isSameAs(iv);
assertThat(bean.stringMap).hasSize(1);
assertThat(bean.integerMap).hasSize(1);
assertThat(bean.stringMap.get("stringValue")).isSameAs(sv);
@@ -2332,8 +2332,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerRepositoryArray[0]).isSameAs(ir);
assertThat(bean.stringRepositoryList).hasSize(1);
assertThat(bean.integerRepositoryList).hasSize(1);
assertThat(bean.stringRepositoryList.get(0)).isSameAs(sr);
assertThat(bean.integerRepositoryList.get(0)).isSameAs(ir);
assertThat(bean.stringRepositoryList).element(0).isSameAs(sr);
assertThat(bean.integerRepositoryList).element(0).isSameAs(ir);
assertThat(bean.stringRepositoryMap).hasSize(1);
assertThat(bean.integerRepositoryMap).hasSize(1);
assertThat(bean.stringRepositoryMap.get("stringRepo")).isSameAs(sr);
@@ -2359,8 +2359,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerRepositoryArray[0]).isSameAs(ir);
assertThat(bean.stringRepositoryList).hasSize(1);
assertThat(bean.integerRepositoryList).hasSize(1);
assertThat(bean.stringRepositoryList.get(0)).isSameAs(sr);
assertThat(bean.integerRepositoryList.get(0)).isSameAs(ir);
assertThat(bean.stringRepositoryList).element(0).isSameAs(sr);
assertThat(bean.integerRepositoryList).element(0).isSameAs(ir);
assertThat(bean.stringRepositoryMap).hasSize(1);
assertThat(bean.integerRepositoryMap).hasSize(1);
assertThat(bean.stringRepositoryMap.get("stringRepo")).isSameAs(sr);
@@ -2385,8 +2385,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerRepositoryArray[0]).isSameAs(gr);
assertThat(bean.stringRepositoryList).hasSize(1);
assertThat(bean.integerRepositoryList).hasSize(1);
assertThat(bean.stringRepositoryList.get(0)).isSameAs(gr);
assertThat(bean.integerRepositoryList.get(0)).isSameAs(gr);
assertThat(bean.stringRepositoryList).element(0).isSameAs(gr);
assertThat(bean.integerRepositoryList).element(0).isSameAs(gr);
assertThat(bean.stringRepositoryMap).hasSize(1);
assertThat(bean.integerRepositoryMap).hasSize(1);
assertThat(bean.stringRepositoryMap.get("genericRepo")).isSameAs(gr);
@@ -2410,8 +2410,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerRepositoryArray[0]).isSameAs(ngr);
assertThat(bean.stringRepositoryList).hasSize(1);
assertThat(bean.integerRepositoryList).hasSize(1);
assertThat(bean.stringRepositoryList.get(0)).isSameAs(ngr);
assertThat(bean.integerRepositoryList.get(0)).isSameAs(ngr);
assertThat(bean.stringRepositoryList).element(0).isSameAs(ngr);
assertThat(bean.integerRepositoryList).element(0).isSameAs(ngr);
assertThat(bean.stringRepositoryMap).hasSize(1);
assertThat(bean.integerRepositoryMap).hasSize(1);
assertThat(bean.stringRepositoryMap.get("simpleRepo")).isSameAs(ngr);
@@ -2438,8 +2438,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerRepositoryArray[0]).isSameAs(gr);
assertThat(bean.stringRepositoryList).hasSize(1);
assertThat(bean.integerRepositoryList).hasSize(1);
assertThat(bean.stringRepositoryList.get(0)).isSameAs(sr);
assertThat(bean.integerRepositoryList.get(0)).isSameAs(gr);
assertThat(bean.stringRepositoryList).element(0).isSameAs(sr);
assertThat(bean.integerRepositoryList).element(0).isSameAs(gr);
assertThat(bean.stringRepositoryMap).hasSize(1);
assertThat(bean.integerRepositoryMap).hasSize(1);
assertThat(bean.stringRepositoryMap.get("stringRepo")).isSameAs(sr);
@@ -2465,8 +2465,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.integerRepositoryArray[0]).isSameAs(ngr);
assertThat(bean.stringRepositoryList).hasSize(1);
assertThat(bean.integerRepositoryList).hasSize(1);
assertThat(bean.stringRepositoryList.get(0)).isSameAs(sr);
assertThat(bean.integerRepositoryList.get(0)).isSameAs(ngr);
assertThat(bean.stringRepositoryList).element(0).isSameAs(sr);
assertThat(bean.integerRepositoryList).element(0).isSameAs(ngr);
assertThat(bean.stringRepositoryMap).hasSize(1);
assertThat(bean.integerRepositoryMap).hasSize(1);
assertThat(bean.stringRepositoryMap.get("stringRepo")).isSameAs(sr);
@@ -85,7 +85,7 @@ public class InjectAnnotationBeanPostProcessorTests {
bf.getBean("testBean");
}
catch (BeanCreationException ex) {
assertThat(ex.getRootCause() instanceof IllegalStateException).isTrue();
assertThat(ex.getRootCause()).isInstanceOf(IllegalStateException.class);
}
}
@@ -195,8 +195,8 @@ public class InjectAnnotationBeanPostProcessorTests {
assertThat(bean.getTestBean3()).isNull();
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBeans()).hasSize(2);
assertThat(bean.getNestedTestBeans().get(0)).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans().get(1)).isSameAs(ntb2);
assertThat(bean.getNestedTestBeans()).element(0).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()).element(1).isSameAs(ntb2);
}
@Test
@@ -222,17 +222,17 @@ public class InjectAnnotationBeanPostProcessorTests {
MapConstructorInjectionBean bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBeanMap()).hasSize(2);
assertThat(bean.getTestBeanMap().keySet().contains("testBean1")).isTrue();
assertThat(bean.getTestBeanMap().keySet().contains("testBean2")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb1)).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb2)).isTrue();
assertThat(bean.getTestBeanMap()).containsKey("testBean1");
assertThat(bean.getTestBeanMap()).containsKey("testBean2");
assertThat(bean.getTestBeanMap()).containsValue(tb1);
assertThat(bean.getTestBeanMap()).containsValue(tb2);
bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBeanMap()).hasSize(2);
assertThat(bean.getTestBeanMap().keySet().contains("testBean1")).isTrue();
assertThat(bean.getTestBeanMap().keySet().contains("testBean2")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb1)).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb2)).isTrue();
assertThat(bean.getTestBeanMap()).containsKey("testBean1");
assertThat(bean.getTestBeanMap()).containsKey("testBean2");
assertThat(bean.getTestBeanMap()).containsValue(tb1);
assertThat(bean.getTestBeanMap()).containsValue(tb2);
}
@Test
@@ -247,17 +247,17 @@ public class InjectAnnotationBeanPostProcessorTests {
MapFieldInjectionBean bean = (MapFieldInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBeanMap()).hasSize(2);
assertThat(bean.getTestBeanMap().keySet().contains("testBean1")).isTrue();
assertThat(bean.getTestBeanMap().keySet().contains("testBean2")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb1)).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb2)).isTrue();
assertThat(bean.getTestBeanMap()).containsKey("testBean1");
assertThat(bean.getTestBeanMap()).containsKey("testBean2");
assertThat(bean.getTestBeanMap()).containsValue(tb1);
assertThat(bean.getTestBeanMap()).containsValue(tb2);
bean = (MapFieldInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBeanMap()).hasSize(2);
assertThat(bean.getTestBeanMap().keySet().contains("testBean1")).isTrue();
assertThat(bean.getTestBeanMap().keySet().contains("testBean2")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb1)).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb2)).isTrue();
assertThat(bean.getTestBeanMap()).containsKey("testBean1");
assertThat(bean.getTestBeanMap()).containsKey("testBean2");
assertThat(bean.getTestBeanMap()).containsValue(tb1);
assertThat(bean.getTestBeanMap()).containsValue(tb2);
}
@Test
@@ -270,14 +270,14 @@ public class InjectAnnotationBeanPostProcessorTests {
MapMethodInjectionBean bean = (MapMethodInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBeanMap()).hasSize(1);
assertThat(bean.getTestBeanMap().keySet().contains("testBean")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb)).isTrue();
assertThat(bean.getTestBeanMap()).containsKey("testBean");
assertThat(bean.getTestBeanMap()).containsValue(tb);
assertThat(bean.getTestBean()).isSameAs(tb);
bean = (MapMethodInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBeanMap()).hasSize(1);
assertThat(bean.getTestBeanMap().keySet().contains("testBean")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb)).isTrue();
assertThat(bean.getTestBeanMap()).containsKey("testBean");
assertThat(bean.getTestBeanMap()).containsValue(tb);
assertThat(bean.getTestBean()).isSameAs(tb);
}
@@ -301,8 +301,8 @@ public class InjectAnnotationBeanPostProcessorTests {
MapMethodInjectionBean bean = (MapMethodInjectionBean) bf.getBean("annotatedBean");
TestBean tb = (TestBean) bf.getBean("testBean1");
assertThat(bean.getTestBeanMap()).hasSize(1);
assertThat(bean.getTestBeanMap().keySet().contains("testBean1")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb)).isTrue();
assertThat(bean.getTestBeanMap()).containsKey("testBean1");
assertThat(bean.getTestBeanMap()).containsValue(tb);
assertThat(bean.getTestBean()).isSameAs(tb);
}
@@ -530,7 +530,7 @@ public class InjectAnnotationBeanPostProcessorTests {
OptionalListFieldInjectionBean bean = (OptionalListFieldInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isPresent();
assertThat(bean.getTestBean().get().get(0)).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean().get()).element(0).isSameAs(bf.getBean("testBean"));
}
@Test
@@ -548,7 +548,7 @@ public class InjectAnnotationBeanPostProcessorTests {
OptionalListMethodInjectionBean bean = (OptionalListMethodInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isPresent();
assertThat(bean.getTestBean().get().get(0)).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean().get()).element(0).isSameAs(bf.getBean("testBean"));
}
@Test
@@ -115,7 +115,7 @@ class AotServicesTests {
AotServices<?> loaded = AotServices
.factories(new TestSpringFactoriesClassLoader("aot-services.factories"))
.load(TestService.class);
assertThat(loaded.iterator().next()).isInstanceOf(TestServiceImpl.class);
assertThat(loaded).element(0).isInstanceOf(TestServiceImpl.class);
}
@Test
@@ -45,6 +45,8 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.testfixture.beans.AnnotatedBean;
import org.springframework.beans.testfixture.beans.GenericBean;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.beans.testfixture.beans.factory.aot.CustomBean;
import org.springframework.beans.testfixture.beans.factory.aot.CustomPropertyValue;
import org.springframework.beans.testfixture.beans.factory.aot.InnerBeanConfiguration;
import org.springframework.beans.testfixture.beans.factory.aot.MockBeanRegistrationsCode;
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBean;
@@ -605,6 +607,23 @@ class BeanDefinitionMethodGeneratorTests {
});
}
@Test
void generateBeanDefinitionMethodWhenCustomPropertyValueUsesCustomDelegate() {
RootBeanDefinition beanDefinition = new RootBeanDefinition(CustomBean.class);
beanDefinition.getPropertyValues().addPropertyValue(
"customPropertyValue", new CustomPropertyValue("test"));
RegisteredBean bean = registerBean(beanDefinition);
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
this.methodGeneratorFactory, bean, "test",
Collections.emptyList());
MethodReference method = generator.generateBeanDefinitionMethod(
this.generationContext, this.beanRegistrationsCode);
compile(method, (actual, compiled) ->
assertThat(actual.getPropertyValues().get("customPropertyValue"))
.isInstanceOfSatisfying(CustomPropertyValue.class, customPropertyValue
-> assertThat(customPropertyValue.value()).isEqualTo("test")));
}
@Test
void generateBeanDefinitionMethodWhenHasAotContributionsAppliesContributions() {
RegisteredBean registeredBean = registerBean(
@@ -18,6 +18,7 @@ package org.springframework.beans.factory.aot;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -28,12 +29,12 @@ import java.util.function.Supplier;
import javax.lang.model.element.Modifier;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.reactivestreams.Publisher;
import org.springframework.aot.generate.GeneratedClass;
import org.springframework.aot.generate.ValueCodeGenerator.Delegate;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.aot.test.generate.TestGenerationContext;
@@ -50,6 +51,7 @@ import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.testfixture.beans.factory.aot.CustomPropertyValue;
import org.springframework.beans.testfixture.beans.factory.aot.DeferredTypeBuilder;
import org.springframework.core.test.tools.Compiled;
import org.springframework.core.test.tools.TestCompiler;
@@ -258,10 +260,10 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
compile((actual, compiled) -> {
ConstructorArgumentValues argumentValues = actual.getConstructorArgumentValues();
List<ValueHolder> values = argumentValues.getGenericArgumentValues();
assertThat(values.get(0)).satisfies(assertValueHolder(String.class, null, null));
assertThat(values.get(1)).satisfies(assertValueHolder(2, Long.class, null));
assertThat(values.get(2)).satisfies(assertValueHolder("value", null, "param1"));
assertThat(values.get(3)).satisfies(assertValueHolder("another", CharSequence.class, "param2"));
assertThat(values).element(0).satisfies(assertValueHolder(String.class, null, null));
assertThat(values).element(1).satisfies(assertValueHolder(2, Long.class, null));
assertThat(values).element(2).satisfies(assertValueHolder("value", null, "param1"));
assertThat(values).element(3).satisfies(assertValueHolder("another", CharSequence.class, "param2"));
assertThat(values).hasSize(4);
assertThat(argumentValues.getIndexedArgumentValues()).isEmpty();
});
@@ -311,7 +313,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
assertThat(actual.getPropertyValues().contains("myService")).isTrue();
assertThat(actual.getPropertyValues().get("myService"))
.isInstanceOfSatisfying(RuntimeBeanReference.class,
beanReference -> assertThat(beanReference.getBeanName()).isEqualTo("test"));
beanReference -> assertThat(beanReference.getBeanName()).isEqualTo("test"));
});
}
@@ -323,7 +325,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
compile((actual, compiled) -> {
Object value = actual.getPropertyValues().get("value");
assertThat(value).isInstanceOf(ManagedList.class);
assertThat(((List<?>) value).get(0)).isInstanceOf(BeanReference.class);
assertThat(((List<?>) value)).element(0).isInstanceOf(BeanReference.class);
});
}
@@ -335,7 +337,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
compile((actual, compiled) -> {
Object value = actual.getPropertyValues().get("value");
assertThat(value).isInstanceOf(ManagedSet.class);
assertThat(((Set<?>) value).iterator().next()).isInstanceOf(BeanReference.class);
assertThat(((Set<?>) value)).element(0).isInstanceOf(BeanReference.class);
});
}
@@ -361,10 +363,25 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
assertThat(actual.getPropertyValues().get("prefix")).isEqualTo("Hello");
assertThat(actual.getPropertyValues().get("name")).isEqualTo("World");
});
assertHasMethodInvokeHints(PropertyValuesFactoryBean.class, "setPrefix", "setName" );
assertHasMethodInvokeHints(PropertyValuesFactoryBean.class, "setPrefix", "setName");
assertHasDeclaredFieldsHint(PropertyValuesFactoryBean.class);
}
@Test
void propertyValuesWhenCustomValuesUsingDelegate() {
this.beanDefinition.setTargetType(PropertyValuesBean.class);
this.beanDefinition.getPropertyValues().add("test", new CustomPropertyValue("test"));
this.beanDefinition.getPropertyValues().add("spring", new CustomPropertyValue("framework"));
compile(value -> true, List.of(new CustomPropertyValue.ValueCodeGeneratorDelegate()), (actual, compiled) -> {
assertThat(actual.getPropertyValues().get("test")).isInstanceOfSatisfying(CustomPropertyValue.class,
customPropertyValue -> assertThat(customPropertyValue.value()).isEqualTo("test"));
assertThat(actual.getPropertyValues().get("spring")).isInstanceOfSatisfying(CustomPropertyValue.class,
customPropertyValue -> assertThat(customPropertyValue.value()).isEqualTo("framework"));
});
assertHasMethodInvokeHints(PropertyValuesBean.class, "setTest", "setSpring");
assertHasDeclaredFieldsHint(PropertyValuesBean.class);
}
@Test
void attributesWhenAllFiltered() {
this.beanDefinition.setAttribute("a", "A");
@@ -412,8 +429,8 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
this.beanDefinition.addQualifier(new AutowireCandidateQualifier("com.example.Another", ChronoUnit.SECONDS));
compile((actual, compiled) -> {
List<AutowireCandidateQualifier> qualifiers = new ArrayList<>(actual.getQualifiers());
assertThat(qualifiers.get(0)).satisfies(isQualifierFor("com.example.Qualifier", "id"));
assertThat(qualifiers.get(1)).satisfies(isQualifierFor("com.example.Another", ChronoUnit.SECONDS));
assertThat(qualifiers).element(0).satisfies(isQualifierFor("com.example.Qualifier", "id"));
assertThat(qualifiers).element(1).satisfies(isQualifierFor("com.example.Another", ChronoUnit.SECONDS));
assertThat(qualifiers).hasSize(2);
});
}
@@ -440,28 +457,36 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
@Nested
class InitDestroyMethodTests {
private final String privateInitMethod = InitDestroyBean.class.getName() + ".privateInit";
private final String privateDestroyMethod = InitDestroyBean.class.getName() + ".privateDestroy";
private static final String privateInitMethod = InitDestroyBean.class.getName() + ".privateInit";
@BeforeEach
void setTargetType() {
beanDefinition.setTargetType(InitDestroyBean.class);
}
private static final String privateDestroyMethod = InitDestroyBean.class.getName() + ".privateDestroy";
@Test
void noInitMethod() {
beanDefinition.setTargetType(InitDestroyBean.class);
compile((beanDef, compiled) -> assertThat(beanDef.getInitMethodNames()).isNull());
}
@Test
void singleInitMethod() {
beanDefinition.setTargetType(InitDestroyBean.class);
beanDefinition.setInitMethodName("init");
compile((beanDef, compiled) -> assertThat(beanDef.getInitMethodNames()).containsExactly("init"));
assertHasMethodInvokeHints(InitDestroyBean.class, "init");
}
@Test
void singleInitMethodFromInterface() {
beanDefinition.setTargetType(InitializableTestBean.class);
beanDefinition.setInitMethodName("initialize");
compile((beanDef, compiled) -> assertThat(beanDef.getInitMethodNames()).containsExactly("initialize"));
assertHasMethodInvokeHints(InitializableTestBean.class, "initialize");
assertHasMethodInvokeHints(Initializable.class, "initialize");
}
@Test
void privateInitMethod() {
beanDefinition.setTargetType(InitDestroyBean.class);
beanDefinition.setInitMethodName(privateInitMethod);
compile((beanDef, compiled) -> assertThat(beanDef.getInitMethodNames()).containsExactly(privateInitMethod));
assertHasMethodInvokeHints(InitDestroyBean.class, "privateInit");
@@ -469,6 +494,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
@Test
void multipleInitMethods() {
beanDefinition.setTargetType(InitDestroyBean.class);
beanDefinition.setInitMethodNames("init", privateInitMethod);
compile((beanDef, compiled) -> assertThat(beanDef.getInitMethodNames()).containsExactly("init", privateInitMethod));
assertHasMethodInvokeHints(InitDestroyBean.class, "init", "privateInit");
@@ -476,20 +502,33 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
@Test
void noDestroyMethod() {
beanDefinition.setTargetType(InitDestroyBean.class);
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).isNull());
assertReflectionOnPublisher();
}
@Test
void singleDestroyMethod() {
beanDefinition.setTargetType(InitDestroyBean.class);
beanDefinition.setDestroyMethodName("destroy");
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).containsExactly("destroy"));
assertHasMethodInvokeHints(InitDestroyBean.class, "destroy");
assertReflectionOnPublisher();
}
@Test
void singleDestroyMethodFromInterface() {
beanDefinition.setTargetType(DisposableTestBean.class);
beanDefinition.setDestroyMethodName("dispose");
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).containsExactly("dispose"));
assertHasMethodInvokeHints(DisposableTestBean.class, "dispose");
assertHasMethodInvokeHints(Disposable.class, "dispose");
assertReflectionOnPublisher();
}
@Test
void privateDestroyMethod() {
beanDefinition.setTargetType(InitDestroyBean.class);
beanDefinition.setDestroyMethodName(privateDestroyMethod);
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).containsExactly(privateDestroyMethod));
assertHasMethodInvokeHints(InitDestroyBean.class, "privateDestroy");
@@ -498,6 +537,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
@Test
void multipleDestroyMethods() {
beanDefinition.setTargetType(InitDestroyBean.class);
beanDefinition.setDestroyMethodNames("destroy", privateDestroyMethod);
compile((beanDef, compiled) -> assertThat(beanDef.getDestroyMethodNames()).containsExactly("destroy", privateDestroyMethod));
assertHasMethodInvokeHints(InitDestroyBean.class, "destroy", "privateDestroy");
@@ -512,8 +552,8 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
private void assertHasMethodInvokeHints(Class<?> beanType, String... methodNames) {
assertThat(methodNames).allMatch(methodName -> RuntimeHintsPredicates.reflection()
.onMethod(beanType, methodName).invoke()
.test(this.generationContext.getRuntimeHints()));
.onMethod(beanType, methodName).invoke()
.test(this.generationContext.getRuntimeHints()));
}
private void assertHasDeclaredFieldsHint(Class<?> beanType) {
@@ -526,12 +566,18 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
compile(attribute -> true, result);
}
private void compile(Predicate<String> attributeFilter, BiConsumer<RootBeanDefinition, Compiled> result) {
private void compile(Predicate<String> attributeFilter,
BiConsumer<RootBeanDefinition, Compiled> result) {
compile(attributeFilter, Collections.emptyList(), result);
}
private void compile(Predicate<String> attributeFilter, List<Delegate> additionalDelegates,
BiConsumer<RootBeanDefinition, Compiled> result) {
DeferredTypeBuilder typeBuilder = new DeferredTypeBuilder();
GeneratedClass generatedClass = this.generationContext.getGeneratedClasses().addForFeature("TestCode", typeBuilder);
BeanDefinitionPropertiesCodeGenerator codeGenerator = new BeanDefinitionPropertiesCodeGenerator(
this.generationContext.getRuntimeHints(), attributeFilter,
generatedClass.getMethods(), (name, value) -> null);
generatedClass.getMethods(), additionalDelegates, (name, value) -> null);
CodeBlock generatedCode = codeGenerator.generateCode(this.beanDefinition);
typeBuilder.set(type -> {
type.addModifiers(Modifier.PUBLIC);
@@ -569,6 +615,31 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
}
interface Initializable {
void initialize();
}
static class InitializableTestBean implements Initializable {
@Override
public void initialize() {
}
}
interface Disposable {
void dispose();
}
static class DisposableTestBean implements Disposable {
@Override
public void dispose() {
}
}
static class PropertyValuesBean {
private Class<?> test;
@@ -36,6 +36,8 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.aot.generate.GeneratedClass;
import org.springframework.aot.generate.ValueCodeGenerator;
import org.springframework.aot.generate.ValueCodeGeneratorDelegates;
import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.RuntimeBeanNameReference;
@@ -47,33 +49,38 @@ import org.springframework.beans.testfixture.beans.factory.aot.DeferredTypeBuild
import org.springframework.core.ResolvableType;
import org.springframework.core.test.tools.Compiled;
import org.springframework.core.test.tools.TestCompiler;
import org.springframework.core.testfixture.aot.generate.value.EnumWithClassBody;
import org.springframework.core.testfixture.aot.generate.value.ExampleClass;
import org.springframework.core.testfixture.aot.generate.value.ExampleClass$$GeneratedBy;
import org.springframework.javapoet.CodeBlock;
import org.springframework.javapoet.MethodSpec;
import org.springframework.javapoet.ParameterizedTypeName;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link BeanDefinitionPropertyValueCodeGenerator}.
* Tests for {@link BeanDefinitionPropertyValueCodeGeneratorDelegates}. This
* also tests that code generated by {@link ValueCodeGeneratorDelegates}
* compiles.
*
* @author Stephane Nicoll
* @author Phillip Webb
* @author Sebastien Deleuze
* @since 6.0
* @see BeanDefinitionPropertyValueCodeGeneratorTests
*/
class BeanDefinitionPropertyValueCodeGeneratorTests {
class BeanDefinitionPropertyValueCodeGeneratorDelegatesTests {
private static BeanDefinitionPropertyValueCodeGenerator createPropertyValuesCodeGenerator(GeneratedClass generatedClass) {
return new BeanDefinitionPropertyValueCodeGenerator(generatedClass.getMethods(), null);
private static ValueCodeGenerator createValueCodeGenerator(GeneratedClass generatedClass) {
return ValueCodeGenerator.with(BeanDefinitionPropertyValueCodeGeneratorDelegates.INSTANCES)
.add(ValueCodeGeneratorDelegates.INSTANCES)
.scoped(generatedClass.getMethods());
}
private void compile(Object value, BiConsumer<Object, Compiled> result) {
TestGenerationContext generationContext = new TestGenerationContext();
DeferredTypeBuilder typeBuilder = new DeferredTypeBuilder();
GeneratedClass generatedClass = generationContext.getGeneratedClasses().addForFeature("TestCode", typeBuilder);
CodeBlock generatedCode = createPropertyValuesCodeGenerator(generatedClass).generateCode(value);
CodeBlock generatedCode = createValueCodeGenerator(generatedClass).generateCode(value);
typeBuilder.set(type -> {
type.addModifiers(Modifier.PUBLIC);
type.addSuperinterface(
@@ -101,90 +108,72 @@ class BeanDefinitionPropertyValueCodeGeneratorTests {
@Test
void generateWhenBoolean() {
compile(true, (instance, compiled) -> {
assertThat(instance).isEqualTo(Boolean.TRUE);
assertThat(compiled.getSourceFile()).contains("true");
});
compile(true, (instance, compiled) ->
assertThat(instance).isEqualTo(Boolean.TRUE));
}
@Test
void generateWhenByte() {
compile((byte) 2, (instance, compiled) -> {
assertThat(instance).isEqualTo((byte) 2);
assertThat(compiled.getSourceFile()).contains("(byte) 2");
});
compile((byte) 2, (instance, compiled) ->
assertThat(instance).isEqualTo((byte) 2));
}
@Test
void generateWhenShort() {
compile((short) 3, (instance, compiled) -> {
assertThat(instance).isEqualTo((short) 3);
assertThat(compiled.getSourceFile()).contains("(short) 3");
});
compile((short) 3, (instance, compiled) ->
assertThat(instance).isEqualTo((short) 3));
}
@Test
void generateWhenInt() {
compile(4, (instance, compiled) -> {
assertThat(instance).isEqualTo(4);
assertThat(compiled.getSourceFile()).contains("return 4;");
});
compile(4, (instance, compiled) ->
assertThat(instance).isEqualTo(4));
}
@Test
void generateWhenLong() {
compile(5L, (instance, compiled) -> {
assertThat(instance).isEqualTo(5L);
assertThat(compiled.getSourceFile()).contains("5L");
});
compile(5L, (instance, compiled) ->
assertThat(instance).isEqualTo(5L));
}
@Test
void generateWhenFloat() {
compile(0.1F, (instance, compiled) -> {
assertThat(instance).isEqualTo(0.1F);
assertThat(compiled.getSourceFile()).contains("0.1F");
});
compile(0.1F, (instance, compiled) ->
assertThat(instance).isEqualTo(0.1F));
}
@Test
void generateWhenDouble() {
compile(0.2, (instance, compiled) -> {
assertThat(instance).isEqualTo(0.2);
assertThat(compiled.getSourceFile()).contains("(double) 0.2");
});
compile(0.2, (instance, compiled) ->
assertThat(instance).isEqualTo(0.2));
}
@Test
void generateWhenChar() {
compile('a', (instance, compiled) -> {
assertThat(instance).isEqualTo('a');
assertThat(compiled.getSourceFile()).contains("'a'");
});
compile('a', (instance, compiled) ->
assertThat(instance).isEqualTo('a'));
}
@Test
void generateWhenSimpleEscapedCharReturnsEscaped() {
testEscaped('\b', "'\\b'");
testEscaped('\t', "'\\t'");
testEscaped('\n', "'\\n'");
testEscaped('\f', "'\\f'");
testEscaped('\r', "'\\r'");
testEscaped('\"', "'\"'");
testEscaped('\'', "'\\''");
testEscaped('\\', "'\\\\'");
testEscaped('\b');
testEscaped('\t');
testEscaped('\n');
testEscaped('\f');
testEscaped('\r');
testEscaped('\"');
testEscaped('\'');
testEscaped('\\');
}
@Test
void generatedWhenUnicodeEscapedCharReturnsEscaped() {
testEscaped('\u007f', "'\\u007f'");
testEscaped('\u007f');
}
private void testEscaped(char value, String expectedSourceContent) {
compile(value, (instance, compiled) -> {
assertThat(instance).isEqualTo(value);
assertThat(compiled.getSourceFile()).contains(expectedSourceContent);
});
private void testEscaped(char value) {
compile(value, (instance, compiled) ->
assertThat(instance).isEqualTo(value));
}
}
@@ -194,10 +183,8 @@ class BeanDefinitionPropertyValueCodeGeneratorTests {
@Test
void generateWhenString() {
compile("test\n", (instance, compiled) -> {
assertThat(instance).isEqualTo("test\n");
assertThat(compiled.getSourceFile()).contains("\n");
});
compile("test\n", (instance, compiled) ->
assertThat(instance).isEqualTo("test\n"));
}
}
@@ -207,10 +194,8 @@ class BeanDefinitionPropertyValueCodeGeneratorTests {
@Test
void generateWhenCharset() {
compile(StandardCharsets.UTF_8, (instance, compiled) -> {
assertThat(instance).isEqualTo(Charset.forName("UTF-8"));
assertThat(compiled.getSourceFile()).contains("\"UTF-8\"");
});
compile(StandardCharsets.UTF_8, (instance, compiled) ->
assertThat(instance).isEqualTo(Charset.forName("UTF-8")));
}
}
@@ -220,18 +205,14 @@ class BeanDefinitionPropertyValueCodeGeneratorTests {
@Test
void generateWhenEnum() {
compile(ChronoUnit.DAYS, (instance, compiled) -> {
assertThat(instance).isEqualTo(ChronoUnit.DAYS);
assertThat(compiled.getSourceFile()).contains("ChronoUnit.DAYS");
});
compile(ChronoUnit.DAYS, (instance, compiled) ->
assertThat(instance).isEqualTo(ChronoUnit.DAYS));
}
@Test
void generateWhenEnumWithClassBody() {
compile(EnumWithClassBody.TWO, (instance, compiled) -> {
assertThat(instance).isEqualTo(EnumWithClassBody.TWO);
assertThat(compiled.getSourceFile()).contains("EnumWithClassBody.TWO");
});
compile(EnumWithClassBody.TWO, (instance, compiled) ->
assertThat(instance).isEqualTo(EnumWithClassBody.TWO));
}
}
@@ -266,18 +247,16 @@ class BeanDefinitionPropertyValueCodeGeneratorTests {
@Test
void generateWhenNoneResolvableType() {
ResolvableType resolvableType = ResolvableType.NONE;
compile(resolvableType, (instance, compiled) -> {
assertThat(instance).isEqualTo(resolvableType);
assertThat(compiled.getSourceFile()).contains("ResolvableType.NONE");
});
compile(resolvableType, (instance, compiled) ->
assertThat(instance).isEqualTo(resolvableType));
}
@Test
void generateWhenGenericResolvableType() {
ResolvableType resolvableType = ResolvableType
.forClassWithGenerics(List.class, String.class);
compile(resolvableType, (instance, compiled) -> assertThat(instance)
.isEqualTo(resolvableType));
compile(resolvableType, (instance, compiled) ->
assertThat(instance).isEqualTo(resolvableType));
}
@Test
@@ -298,28 +277,22 @@ class BeanDefinitionPropertyValueCodeGeneratorTests {
@Test
void generateWhenPrimitiveArray() {
byte[] bytes = { 0, 1, 2 };
compile(bytes, (instance, compiler) -> {
assertThat(instance).isEqualTo(bytes);
assertThat(compiler.getSourceFile()).contains("new byte[]");
});
compile(bytes, (instance, compiler) ->
assertThat(instance).isEqualTo(bytes));
}
@Test
void generateWhenWrapperArray() {
Byte[] bytes = { 0, 1, 2 };
compile(bytes, (instance, compiler) -> {
assertThat(instance).isEqualTo(bytes);
assertThat(compiler.getSourceFile()).contains("new Byte[]");
});
compile(bytes, (instance, compiler) ->
assertThat(instance).isEqualTo(bytes));
}
@Test
void generateWhenClassArray() {
Class<?>[] classes = new Class<?>[] { InputStream.class, OutputStream.class };
compile(classes, (instance, compiler) -> {
assertThat(instance).isEqualTo(classes);
assertThat(compiler.getSourceFile()).contains("new Class[]");
});
compile(classes, (instance, compiler) ->
assertThat(instance).isEqualTo(classes));
}
}
@@ -402,10 +375,7 @@ class BeanDefinitionPropertyValueCodeGeneratorTests {
@Test
void generateWhenEmptyList() {
List<String> list = List.of();
compile(list, (instance, compiler) -> {
assertThat(instance).isEqualTo(list);
assertThat(compiler.getSourceFile()).contains("Collections.emptyList();");
});
compile(list, (instance, compiler) -> assertThat(instance).isEqualTo(list));
}
}
@@ -423,20 +393,14 @@ class BeanDefinitionPropertyValueCodeGeneratorTests {
@Test
void generateWhenEmptySet() {
Set<String> set = Set.of();
compile(set, (instance, compiler) -> {
assertThat(instance).isEqualTo(set);
assertThat(compiler.getSourceFile()).contains("Collections.emptySet();");
});
compile(set, (instance, compiler) -> assertThat(instance).isEqualTo(set));
}
@Test
void generateWhenLinkedHashSet() {
Set<String> set = new LinkedHashSet<>(List.of("a", "b", "c"));
compile(set, (instance, compiler) -> {
assertThat(instance).isEqualTo(set).isInstanceOf(LinkedHashSet.class);
assertThat(compiler.getSourceFile())
.contains("new LinkedHashSet(List.of(");
});
compile(set, (instance, compiler) ->
assertThat(instance).isEqualTo(set).isInstanceOf(LinkedHashSet.class));
}
@Test
@@ -453,10 +417,8 @@ class BeanDefinitionPropertyValueCodeGeneratorTests {
@Test
void generateWhenSmallMap() {
Map<String, String> map = Map.of("k1", "v1", "k2", "v2");
compile(map, (instance, compiler) -> {
assertThat(instance).isEqualTo(map);
assertThat(compiler.getSourceFile()).contains("Map.of(");
});
compile(map, (instance, compiler) ->
assertThat(instance).isEqualTo(map));
}
@Test
@@ -465,10 +427,7 @@ class BeanDefinitionPropertyValueCodeGeneratorTests {
for (int i = 1; i <= 11; i++) {
map.put("k" + i, "v" + i);
}
compile(map, (instance, compiler) -> {
assertThat(instance).isEqualTo(map);
assertThat(compiler.getSourceFile()).contains("Map.ofEntries(");
});
compile(map, (instance, compiler) -> assertThat(instance).isEqualTo(map));
}
@Test
@@ -518,47 +477,4 @@ class BeanDefinitionPropertyValueCodeGeneratorTests {
}
@Nested
static class ExceptionTests {
@Test
void generateWhenUnsupportedDataTypeThrowsException() {
SampleValue sampleValue = new SampleValue("one");
assertThatIllegalArgumentException().isThrownBy(() -> generateCode(sampleValue))
.withMessageContaining("Failed to generate code for")
.withMessageContaining(sampleValue.toString())
.withMessageContaining(SampleValue.class.getName())
.havingCause()
.withMessageContaining("Code generation does not support")
.withMessageContaining(SampleValue.class.getName());
}
@Test
void generateWhenListOfUnsupportedElement() {
SampleValue one = new SampleValue("one");
SampleValue two = new SampleValue("two");
List<SampleValue> list = List.of(one, two);
assertThatIllegalArgumentException().isThrownBy(() -> generateCode(list))
.withMessageContaining("Failed to generate code for")
.withMessageContaining(list.toString())
.withMessageContaining(list.getClass().getName())
.havingCause()
.withMessageContaining("Failed to generate code for")
.withMessageContaining(one.toString())
.withMessageContaining("?")
.havingCause()
.withMessageContaining("Code generation does not support ?");
}
private void generateCode(Object value) {
TestGenerationContext context = new TestGenerationContext();
GeneratedClass generatedClass = context.getGeneratedClasses()
.addForFeature("Test", type -> {});
createPropertyValuesCodeGenerator(generatedClass).generateCode(value);
}
record SampleValue(String name) {}
}
}
@@ -66,7 +66,7 @@ public class CustomScopeConfigurerTests {
CustomScopeConfigurer figurer = new CustomScopeConfigurer();
figurer.setScopes(scopes);
figurer.postProcessBeanFactory(factory);
assertThat(factory.getRegisteredScope(FOO_SCOPE) instanceof NoOpScope).isTrue();
assertThat(factory.getRegisteredScope(FOO_SCOPE)).isInstanceOf(NoOpScope.class);
}
@Test
@@ -76,7 +76,7 @@ public class CustomScopeConfigurerTests {
CustomScopeConfigurer figurer = new CustomScopeConfigurer();
figurer.setScopes(scopes);
figurer.postProcessBeanFactory(factory);
assertThat(factory.getRegisteredScope(FOO_SCOPE) instanceof NoOpScope).isTrue();
assertThat(factory.getRegisteredScope(FOO_SCOPE)).isInstanceOf(NoOpScope.class);
}
@Test
@@ -49,7 +49,7 @@ public class PropertyPathFactoryBeanTests {
assertThat(xbf.getType("otb.spouse")).isEqualTo(ITestBean.class);
Object result1 = xbf.getBean("otb.spouse");
Object result2 = xbf.getBean("otb.spouse");
assertThat(result1 instanceof TestBean).isTrue();
assertThat(result1).isInstanceOf(TestBean.class);
assertThat(result1).isSameAs(result2);
assertThat(((TestBean) result1).getAge()).isEqualTo(99);
}
@@ -63,9 +63,9 @@ public class PropertyPathFactoryBeanTests {
Object result1 = xbf.getBean("tb.spouse");
Object result2 = xbf.getBean("propertyPath3");
Object result3 = xbf.getBean("propertyPath3");
assertThat(result1 instanceof TestBean).isTrue();
assertThat(result2 instanceof TestBean).isTrue();
assertThat(result3 instanceof TestBean).isTrue();
assertThat(result1).isInstanceOf(TestBean.class);
assertThat(result2).isInstanceOf(TestBean.class);
assertThat(result3).isInstanceOf(TestBean.class);
assertThat(((TestBean) result1).getAge()).isEqualTo(11);
assertThat(((TestBean) result2).getAge()).isEqualTo(11);
assertThat(((TestBean) result3).getAge()).isEqualTo(11);
@@ -89,7 +89,7 @@ public class PropertyPathFactoryBeanTests {
TestBean spouse = (TestBean) xbf.getBean("otb.spouse");
TestBean tbWithInner = (TestBean) xbf.getBean("tbWithInner");
assertThat(tbWithInner.getSpouse()).isSameAs(spouse);
assertThat(!tbWithInner.getFriends().isEmpty()).isTrue();
assertThat(tbWithInner.getFriends()).isNotEmpty();
assertThat(tbWithInner.getFriends().iterator().next()).isSameAs(spouse);
}
@@ -405,7 +405,7 @@ public class PropertyResourceConfigurerTests {
assertThat(tb2.getStringArray()[0]).isEqualTo(System.getProperty("os.name"));
assertThat(tb2.getStringArray()[1]).isEqualTo("98");
assertThat(tb2.getFriends()).hasSize(2);
assertThat(tb2.getFriends().iterator().next()).isEqualTo("na98me");
assertThat(tb2.getFriends()).element(0).isEqualTo("na98me");
assertThat(tb2.getFriends().toArray()[1]).isEqualTo(tb2);
assertThat(tb2.getSomeSet()).hasSize(3);
assertThat(tb2.getSomeSet().contains("na98me")).isTrue();
@@ -198,10 +198,10 @@ public class ServiceLocatorFactoryBeanTests {
assertThat(testBean3).isNotSameAs(testBean2);
assertThat(testBean4).isNotSameAs(testBean2);
assertThat(testBean4).isNotSameAs(testBean3);
assertThat(testBean1 instanceof ExtendedTestService).isFalse();
assertThat(testBean2 instanceof ExtendedTestService).isFalse();
assertThat(testBean3 instanceof ExtendedTestService).isFalse();
assertThat(testBean4 instanceof ExtendedTestService).isTrue();
assertThat(testBean1).isNotInstanceOf(ExtendedTestService.class);
assertThat(testBean2).isNotInstanceOf(ExtendedTestService.class);
assertThat(testBean3).isNotInstanceOf(ExtendedTestService.class);
assertThat(testBean4).isInstanceOf(ExtendedTestService.class);
}
@Test
@@ -99,7 +99,7 @@ public class YamlMapFactoryBeanTests {
assertThat(map).hasSize(1);
assertThat(map.containsKey("foo")).isTrue();
Object object = map.get("foo");
assertThat(object instanceof LinkedHashMap).isTrue();
assertThat(object).isInstanceOf(LinkedHashMap.class);
@SuppressWarnings("unchecked")
Map<String, Object> sub = (Map<String, Object>) object;
assertThat(sub.containsKey("key1.key2")).isTrue();
@@ -114,7 +114,7 @@ public class YamlMapFactoryBeanTests {
assertThat(map).hasSize(1);
assertThat(map.containsKey("foo")).isTrue();
Object object = map.get("foo");
assertThat(object instanceof LinkedHashMap).isTrue();
assertThat(object).isInstanceOf(LinkedHashMap.class);
@SuppressWarnings("unchecked")
Map<String, Object> sub = (Map<String, Object>) object;
assertThat(sub).hasSize(1);
@@ -48,7 +48,7 @@ class ServiceLoaderTests {
bd.getPropertyValues().add("serviceType", DocumentBuilderFactory.class.getName());
bf.registerBeanDefinition("service", bd);
ServiceLoader<?> serviceLoader = (ServiceLoader<?>) bf.getBean("service");
assertThat(serviceLoader.iterator().next() instanceof DocumentBuilderFactory).isTrue();
assertThat(serviceLoader).element(0).isInstanceOf(DocumentBuilderFactory.class);
}
@Test
@@ -57,7 +57,7 @@ class ServiceLoaderTests {
RootBeanDefinition bd = new RootBeanDefinition(ServiceFactoryBean.class);
bd.getPropertyValues().add("serviceType", DocumentBuilderFactory.class.getName());
bf.registerBeanDefinition("service", bd);
assertThat(bf.getBean("service") instanceof DocumentBuilderFactory).isTrue();
assertThat(bf.getBean("service")).isInstanceOf(DocumentBuilderFactory.class);
}
@Test
@@ -67,7 +67,7 @@ class ServiceLoaderTests {
bd.getPropertyValues().add("serviceType", DocumentBuilderFactory.class.getName());
bf.registerBeanDefinition("service", bd);
List<?> serviceList = (List<?>) bf.getBean("service");
assertThat(serviceList.get(0) instanceof DocumentBuilderFactory).isTrue();
assertThat(serviceList).element(0).isInstanceOf(DocumentBuilderFactory.class);
}
}
@@ -35,13 +35,13 @@ public class BeanDefinitionTests {
bd.setLazyInit(true);
bd.setScope("request");
RootBeanDefinition otherBd = new RootBeanDefinition(TestBean.class);
assertThat(!bd.equals(otherBd)).isTrue();
assertThat(!otherBd.equals(bd)).isTrue();
assertThat(bd).isNotEqualTo(otherBd);
assertThat(otherBd).isNotEqualTo(bd);
otherBd.setAbstract(true);
otherBd.setLazyInit(true);
otherBd.setScope("request");
assertThat(bd.equals(otherBd)).isTrue();
assertThat(otherBd.equals(bd)).isTrue();
assertThat(bd).isEqualTo(otherBd);
assertThat(otherBd).isEqualTo(bd);
assertThat(bd.hashCode()).isEqualTo(otherBd.hashCode());
}
@@ -52,14 +52,14 @@ public class BeanDefinitionTests {
bd.getPropertyValues().add("age", "99");
RootBeanDefinition otherBd = new RootBeanDefinition(TestBean.class);
otherBd.getPropertyValues().add("name", "myName");
assertThat(!bd.equals(otherBd)).isTrue();
assertThat(!otherBd.equals(bd)).isTrue();
assertThat(bd).isNotEqualTo(otherBd);
assertThat(otherBd).isNotEqualTo(bd);
otherBd.getPropertyValues().add("age", "11");
assertThat(!bd.equals(otherBd)).isTrue();
assertThat(!otherBd.equals(bd)).isTrue();
assertThat(bd).isNotEqualTo(otherBd);
assertThat(otherBd).isNotEqualTo(bd);
otherBd.getPropertyValues().add("age", "99");
assertThat(bd.equals(otherBd)).isTrue();
assertThat(otherBd.equals(bd)).isTrue();
assertThat(bd).isEqualTo(otherBd);
assertThat(otherBd).isEqualTo(bd);
assertThat(bd.hashCode()).isEqualTo(otherBd.hashCode());
}
@@ -70,14 +70,14 @@ public class BeanDefinitionTests {
bd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5);
RootBeanDefinition otherBd = new RootBeanDefinition(TestBean.class);
otherBd.getConstructorArgumentValues().addGenericArgumentValue("test");
assertThat(!bd.equals(otherBd)).isTrue();
assertThat(!otherBd.equals(bd)).isTrue();
assertThat(bd).isNotEqualTo(otherBd);
assertThat(otherBd).isNotEqualTo(bd);
otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 9);
assertThat(!bd.equals(otherBd)).isTrue();
assertThat(!otherBd.equals(bd)).isTrue();
assertThat(bd).isNotEqualTo(otherBd);
assertThat(otherBd).isNotEqualTo(bd);
otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5);
assertThat(bd.equals(otherBd)).isTrue();
assertThat(otherBd.equals(bd)).isTrue();
assertThat(bd).isEqualTo(otherBd);
assertThat(otherBd).isEqualTo(bd);
assertThat(bd.hashCode()).isEqualTo(otherBd.hashCode());
}
@@ -89,14 +89,14 @@ public class BeanDefinitionTests {
RootBeanDefinition otherBd = new RootBeanDefinition(TestBean.class);
otherBd.getConstructorArgumentValues().addGenericArgumentValue("test", "int");
otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5);
assertThat(!bd.equals(otherBd)).isTrue();
assertThat(!otherBd.equals(bd)).isTrue();
assertThat(bd).isNotEqualTo(otherBd);
assertThat(otherBd).isNotEqualTo(bd);
otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5, "int");
assertThat(!bd.equals(otherBd)).isTrue();
assertThat(!otherBd.equals(bd)).isTrue();
assertThat(bd).isNotEqualTo(otherBd);
assertThat(otherBd).isNotEqualTo(bd);
otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5, "long");
assertThat(bd.equals(otherBd)).isTrue();
assertThat(otherBd.equals(bd)).isTrue();
assertThat(bd).isEqualTo(otherBd);
assertThat(otherBd).isEqualTo(bd);
assertThat(bd.hashCode()).isEqualTo(otherBd.hashCode());
}
@@ -111,21 +111,21 @@ public class BeanDefinitionTests {
otherBd.setScope("request");
otherBd.setAbstract(true);
otherBd.setLazyInit(true);
assertThat(!bd.equals(otherBd)).isTrue();
assertThat(!otherBd.equals(bd)).isTrue();
assertThat(bd).isNotEqualTo(otherBd);
assertThat(otherBd).isNotEqualTo(bd);
otherBd.setParentName("parent");
assertThat(bd.equals(otherBd)).isTrue();
assertThat(otherBd.equals(bd)).isTrue();
assertThat(bd).isEqualTo(otherBd);
assertThat(otherBd).isEqualTo(bd);
assertThat(bd.hashCode()).isEqualTo(otherBd.hashCode());
bd.getPropertyValues();
assertThat(bd.equals(otherBd)).isTrue();
assertThat(otherBd.equals(bd)).isTrue();
assertThat(bd).isEqualTo(otherBd);
assertThat(otherBd).isEqualTo(bd);
assertThat(bd.hashCode()).isEqualTo(otherBd.hashCode());
bd.getConstructorArgumentValues();
assertThat(bd.equals(otherBd)).isTrue();
assertThat(otherBd.equals(bd)).isTrue();
assertThat(bd).isEqualTo(otherBd);
assertThat(otherBd).isEqualTo(bd);
assertThat(bd.hashCode()).isEqualTo(otherBd.hashCode());
}
@@ -137,14 +137,14 @@ public class BeanDefinitionTests {
bd.setScope("request");
BeanDefinitionHolder holder = new BeanDefinitionHolder(bd, "bd");
RootBeanDefinition otherBd = new RootBeanDefinition(TestBean.class);
assertThat(!bd.equals(otherBd)).isTrue();
assertThat(!otherBd.equals(bd)).isTrue();
assertThat(bd).isNotEqualTo(otherBd);
assertThat(otherBd).isNotEqualTo(bd);
otherBd.setAbstract(true);
otherBd.setLazyInit(true);
otherBd.setScope("request");
BeanDefinitionHolder otherHolder = new BeanDefinitionHolder(bd, "bd");
assertThat(holder.equals(otherHolder)).isTrue();
assertThat(otherHolder.equals(holder)).isTrue();
assertThat(holder).isEqualTo(otherHolder);
assertThat(otherHolder).isEqualTo(holder);
assertThat(holder.hashCode()).isEqualTo(otherHolder.hashCode());
}
@@ -564,8 +564,8 @@ class BeanFactoryGenericsTests {
new ClassPathResource("genericBeanTests.xml", getClass()));
GenericIntegerBean gb = (GenericIntegerBean) bf.getBean("integerBean");
assertThat(gb.getGenericProperty()).isEqualTo(10);
assertThat(gb.getGenericListProperty().get(0)).isEqualTo(20);
assertThat(gb.getGenericListProperty().get(1)).isEqualTo(30);
assertThat(gb.getGenericListProperty()).element(0).isEqualTo(20);
assertThat(gb.getGenericListProperty()).element(1).isEqualTo(30);
}
@Test
@@ -574,9 +574,9 @@ class BeanFactoryGenericsTests {
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
new ClassPathResource("genericBeanTests.xml", getClass()));
GenericSetOfIntegerBean gb = (GenericSetOfIntegerBean) bf.getBean("setOfIntegerBean");
assertThat(gb.getGenericProperty().iterator().next()).isEqualTo(10);
assertThat(gb.getGenericListProperty().get(0).iterator().next()).isEqualTo(20);
assertThat(gb.getGenericListProperty().get(1).iterator().next()).isEqualTo(30);
assertThat(gb.getGenericProperty()).element(0).isEqualTo(10);
assertThat(gb.getGenericListProperty().get(0)).element(0).isEqualTo(20);
assertThat(gb.getGenericListProperty().get(1)).element(0).isEqualTo(30);
}
@Test
@@ -811,18 +811,18 @@ class BeanFactoryGenericsTests {
resolved.add(instance);
}
assertThat(resolved).hasSize(2);
assertThat(resolved.get(0)).isSameAs(bf.getBean("store1"));
assertThat(resolved.get(1)).isSameAs(bf.getBean("store2"));
assertThat(resolved).element(0).isSameAs(bf.getBean("store1"));
assertThat(resolved).element(1).isSameAs(bf.getBean("store2"));
resolved = numberStoreProvider.stream().toList();
assertThat(resolved).hasSize(2);
assertThat(resolved.get(0)).isSameAs(bf.getBean("store1"));
assertThat(resolved.get(1)).isSameAs(bf.getBean("store2"));
assertThat(resolved).element(0).isSameAs(bf.getBean("store1"));
assertThat(resolved).element(1).isSameAs(bf.getBean("store2"));
resolved = numberStoreProvider.orderedStream().toList();
assertThat(resolved).hasSize(2);
assertThat(resolved.get(0)).isSameAs(bf.getBean("store2"));
assertThat(resolved.get(1)).isSameAs(bf.getBean("store1"));
assertThat(resolved).element(0).isSameAs(bf.getBean("store2"));
assertThat(resolved).element(1).isSameAs(bf.getBean("store1"));
resolved = new ArrayList<>();
for (NumberStore<Double> instance : doubleStoreProvider) {
@@ -865,8 +865,8 @@ class BeanFactoryGenericsTests {
ObjectProvider<NumberStore<?>> numberStoreProvider = bf.getBeanProvider(ResolvableType.forClass(NumberStore.class));
List<NumberStore<?>> resolved = numberStoreProvider.orderedStream().toList();
assertThat(resolved).hasSize(2);
assertThat(resolved.get(0)).isSameAs(bf.getBean("store2"));
assertThat(resolved.get(1)).isSameAs(bf.getBean("store1"));
assertThat(resolved).element(0).isSameAs(bf.getBean("store2"));
assertThat(resolved).element(1).isSameAs(bf.getBean("store1"));
}
@@ -59,12 +59,10 @@ public class Spr8954Tests {
@SuppressWarnings("rawtypes")
Map<String, FactoryBean> fbBeans = bf.getBeansOfType(FactoryBean.class);
assertThat(fbBeans).hasSize(1);
assertThat(fbBeans.keySet()).contains("&foo");
assertThat(fbBeans).containsOnlyKeys("&foo");
Map<String, AnInterface> aiBeans = bf.getBeansOfType(AnInterface.class);
assertThat(aiBeans).hasSize(1);
assertThat(aiBeans.keySet()).contains("&foo");
assertThat(aiBeans).containsOnlyKeys("&foo");
}
@Test
@@ -73,12 +71,10 @@ public class Spr8954Tests {
@SuppressWarnings("rawtypes")
Map<String, FactoryBean> fbBeans = bf.getBeansOfType(FactoryBean.class);
assertThat(fbBeans.size()).isEqualTo(1);
assertThat(fbBeans.keySet().iterator().next()).isEqualTo("&foo");
assertThat(fbBeans).containsOnlyKeys("&foo");
Map<String, AnInterface> aiBeans = bf.getBeansOfType(AnInterface.class);
assertThat(aiBeans).hasSize(1);
assertThat(aiBeans.keySet()).contains("&foo");
assertThat(aiBeans).containsOnlyKeys("&foo");
}
/**
@@ -90,8 +86,7 @@ public class Spr8954Tests {
assertThat(Arrays.asList(names)).contains("&foo");
Map<String, AnInterface> beans = bf.getBeansOfType(AnInterface.class, false, false);
assertThat(beans).hasSize(1);
assertThat(beans.keySet()).contains("&foo");
assertThat(beans).containsOnlyKeys("&foo");
}
@@ -55,9 +55,9 @@ public class CollectionMergingTests {
TestBean bean = (TestBean) this.beanFactory.getBean("childWithList");
List<?> list = bean.getSomeList();
assertThat(list).as("Incorrect size").hasSize(3);
assertThat(list.get(0)).isEqualTo("Rob Harrop");
assertThat(list.get(1)).isEqualTo("Rod Johnson");
assertThat(list.get(2)).isEqualTo("Juergen Hoeller");
assertThat(list).element(0).isEqualTo("Rob Harrop");
assertThat(list).element(1).isEqualTo("Rod Johnson");
assertThat(list).element(2).isEqualTo("Juergen Hoeller");
}
@Test
@@ -66,7 +66,7 @@ public class CollectionMergingTests {
List<?> list = bean.getSomeList();
assertThat(list).isNotNull();
assertThat(list).hasSize(3);
assertThat(list.get(2) instanceof TestBean).isTrue();
assertThat(list.get(2)).isInstanceOf(TestBean.class);
}
@Test
@@ -87,7 +87,7 @@ public class CollectionMergingTests {
Iterator it = set.iterator();
it.next();
Object o = it.next();
assertThat(o instanceof TestBean).isTrue();
assertThat(o).isInstanceOf(TestBean.class);
assertThat(((TestBean) o).getName()).isEqualTo("Sally");
}
@@ -108,7 +108,7 @@ public class CollectionMergingTests {
assertThat(map).isNotNull();
assertThat(map).hasSize(2);
assertThat(map.get("Rob")).isNotNull();
assertThat(map.get("Rob") instanceof TestBean).isTrue();
assertThat(map.get("Rob")).isInstanceOf(TestBean.class);
assertThat(((TestBean) map.get("Rob")).getName()).isEqualTo("Sally");
}
@@ -127,9 +127,9 @@ public class CollectionMergingTests {
TestBean bean = (TestBean) this.beanFactory.getBean("childWithListInConstructor");
List<?> list = bean.getSomeList();
assertThat(list).as("Incorrect size").hasSize(3);
assertThat(list.get(0)).isEqualTo("Rob Harrop");
assertThat(list.get(1)).isEqualTo("Rod Johnson");
assertThat(list.get(2)).isEqualTo("Juergen Hoeller");
assertThat(list).element(0).isEqualTo("Rob Harrop");
assertThat(list).element(1).isEqualTo("Rod Johnson");
assertThat(list).element(2).isEqualTo("Juergen Hoeller");
}
@Test
@@ -138,8 +138,8 @@ public class CollectionMergingTests {
List<?> list = bean.getSomeList();
assertThat(list).isNotNull();
assertThat(list).hasSize(3);
assertThat(list.get(2)).isNotNull();
assertThat(list.get(2) instanceof TestBean).isTrue();
assertThat(list).element(2).isNotNull();
assertThat(list.get(2)).isInstanceOf(TestBean.class);
}
@Test
@@ -160,7 +160,7 @@ public class CollectionMergingTests {
Iterator it = set.iterator();
it.next();
Object o = it.next();
assertThat(o instanceof TestBean).isTrue();
assertThat(o).isInstanceOf(TestBean.class);
assertThat(((TestBean) o).getName()).isEqualTo("Sally");
}
@@ -180,7 +180,7 @@ public class CollectionMergingTests {
Map<?, ?> map = bean.getSomeMap();
assertThat(map).isNotNull();
assertThat(map).hasSize(2);
assertThat(map.get("Rob") instanceof TestBean).isTrue();
assertThat(map.get("Rob")).isInstanceOf(TestBean.class);
assertThat(((TestBean) map.get("Rob")).getName()).isEqualTo("Sally");
}
@@ -85,8 +85,8 @@ public class CollectionsWithDefaultTypesTests {
List l = (List) jumble.getJumble();
assertThat(l.get(0).equals("literal")).isTrue();
Integer[] array1 = (Integer[]) l.get(1);
assertThat(array1[0].equals(2)).isTrue();
assertThat(array1[1].equals(4)).isTrue();
assertThat(array1[0]).isEqualTo(2);
assertThat(array1[1]).isEqualTo(4);
int[] array2 = (int[]) l.get(2);
assertThat(array2[0]).isEqualTo(3);
assertThat(array2[1]).isEqualTo(5);
@@ -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.
@@ -61,7 +61,7 @@ class EventPublicationTests {
void defaultsEventReceived() throws Exception {
List<DefaultsDefinition> defaultsList = this.eventListener.getDefaults();
assertThat(defaultsList).isNotEmpty();
assertThat(defaultsList.get(0)).isInstanceOf(DocumentDefaultsDefinition.class);
assertThat(defaultsList).element(0).isInstanceOf(DocumentDefaultsDefinition.class);
DocumentDefaultsDefinition defaults = (DocumentDefaultsDefinition) defaultsList.get(0);
assertThat(defaults.getLazyInit()).isEqualTo("true");
assertThat(defaults.getAutowire()).isEqualTo("constructor");
@@ -199,8 +199,8 @@ public class FactoryMethodTests {
assertThat(xbf.getType("externalFactoryMethodWithoutArgs")).isEqualTo(TestBean.class);
assertThat(xbf.getType("externalFactoryMethodWithArgs")).isEqualTo(TestBean.class);
String[] names = xbf.getBeanNamesForType(TestBean.class);
assertThat(Arrays.asList(names).contains("externalFactoryMethodWithoutArgs")).isTrue();
assertThat(Arrays.asList(names).contains("externalFactoryMethodWithArgs")).isTrue();
assertThat(Arrays.asList(names)).contains("externalFactoryMethodWithoutArgs");
assertThat(Arrays.asList(names)).contains("externalFactoryMethodWithArgs");
TestBean tb = (TestBean) xbf.getBean("externalFactoryMethodWithoutArgs");
assertThat(tb.getAge()).isEqualTo(2);
@@ -212,8 +212,8 @@ public class FactoryMethodTests {
assertThat(xbf.getType("externalFactoryMethodWithoutArgs")).isEqualTo(TestBean.class);
assertThat(xbf.getType("externalFactoryMethodWithArgs")).isEqualTo(TestBean.class);
names = xbf.getBeanNamesForType(TestBean.class);
assertThat(Arrays.asList(names).contains("externalFactoryMethodWithoutArgs")).isTrue();
assertThat(Arrays.asList(names).contains("externalFactoryMethodWithArgs")).isTrue();
assertThat(Arrays.asList(names)).contains("externalFactoryMethodWithoutArgs");
assertThat(Arrays.asList(names)).contains("externalFactoryMethodWithArgs");
}
@Test
@@ -337,16 +337,16 @@ public class FactoryMethodTests {
// Check that listInstance is not considered a bean of type FactoryMethods.
assertThat(List.class.isAssignableFrom(xbf.getType("listInstance"))).isTrue();
String[] names = xbf.getBeanNamesForType(FactoryMethods.class);
assertThat(Arrays.asList(names).contains("listInstance")).isFalse();
assertThat(Arrays.asList(names)).doesNotContain("listInstance");
names = xbf.getBeanNamesForType(List.class);
assertThat(Arrays.asList(names).contains("listInstance")).isTrue();
assertThat(Arrays.asList(names)).contains("listInstance");
xbf.preInstantiateSingletons();
assertThat(List.class.isAssignableFrom(xbf.getType("listInstance"))).isTrue();
names = xbf.getBeanNamesForType(FactoryMethods.class);
assertThat(Arrays.asList(names).contains("listInstance")).isFalse();
assertThat(Arrays.asList(names)).doesNotContain("listInstance");
names = xbf.getBeanNamesForType(List.class);
assertThat(Arrays.asList(names).contains("listInstance")).isTrue();
assertThat(Arrays.asList(names)).contains("listInstance");
List<?> list = (List<?>) xbf.getBean("listInstance");
assertThat(list).isEqualTo(Collections.EMPTY_LIST);
}
@@ -126,7 +126,7 @@ public class UtilNamespaceHandlerTests {
@Test
void testSimpleList() {
List<?> list = (List) this.beanFactory.getBean("simpleList");
assertThat(list.get(0)).isEqualTo("Rob Harrop");
assertThat(list).element(0).isEqualTo("Rob Harrop");
List<?> list2 = (List) this.beanFactory.getBean("simpleList");
assertThat(list).isSameAs(list2);
}
@@ -134,9 +134,9 @@ public class UtilNamespaceHandlerTests {
@Test
void testScopedList() {
List<?> list = (List) this.beanFactory.getBean("scopedList");
assertThat(list.get(0)).isEqualTo("Rob Harrop");
assertThat(list).element(0).isEqualTo("Rob Harrop");
List<?> list2 = (List) this.beanFactory.getBean("scopedList");
assertThat(list2.get(0)).isEqualTo("Rob Harrop");
assertThat(list2).element(0).isEqualTo("Rob Harrop");
assertThat(list).isNotSameAs(list2);
}
@@ -177,7 +177,7 @@ public class UtilNamespaceHandlerTests {
List<?> list = bean.getSomeList();
assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo("foo");
assertThat(list).element(0).isEqualTo("foo");
Set<?> set = bean.getSomeSet();
assertThat(set).hasSize(1);
@@ -208,7 +208,7 @@ public class UtilNamespaceHandlerTests {
List<?> list = bean.getSomeList();
assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo("foo");
assertThat(list).element(0).isEqualTo("foo");
Set<?> set = bean.getSomeSet();
assertThat(set).hasSize(1);
@@ -229,7 +229,7 @@ public class UtilNamespaceHandlerTests {
List<?> list = bean.getSomeList();
assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo(Integer.MIN_VALUE);
assertThat(list).element(0).isEqualTo(Integer.MIN_VALUE);
Set<?> set = bean.getSomeSet();
assertThat(set).hasSize(2);
@@ -255,7 +255,7 @@ public class UtilNamespaceHandlerTests {
List<?> list = bean.getSomeList();
assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo(bean);
assertThat(list).element(0).isEqualTo(bean);
Set<?> set = bean.getSomeSet();
assertThat(set).hasSize(1);
@@ -274,7 +274,7 @@ public class UtilNamespaceHandlerTests {
List<?> list = bean.getSomeList();
assertThat(Proxy.isProxyClass(list.getClass())).isTrue();
assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo(bean);
assertThat(list).element(0).isEqualTo(bean);
Set<?> set = bean.getSomeSet();
assertThat(Proxy.isProxyClass(set.getClass())).isFalse();
@@ -295,7 +295,7 @@ public class UtilNamespaceHandlerTests {
List<?> list = bean.getSomeList();
assertThat(Proxy.isProxyClass(list.getClass())).isFalse();
assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo(bean);
assertThat(list).element(0).isEqualTo(bean);
Set<?> set = bean.getSomeSet();
assertThat(Proxy.isProxyClass(set.getClass())).isTrue();
@@ -316,7 +316,7 @@ public class UtilNamespaceHandlerTests {
List<?> list = bean.getSomeList();
assertThat(Proxy.isProxyClass(list.getClass())).isFalse();
assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo(bean);
assertThat(list).element(0).isEqualTo(bean);
Set<?> set = bean.getSomeSet();
assertThat(Proxy.isProxyClass(set.getClass())).isFalse();
@@ -70,17 +70,17 @@ public class XmlBeanCollectionTests {
ListFactoryBean listFactory = new ListFactoryBean();
listFactory.setSourceList(new LinkedList());
listFactory.afterPropertiesSet();
assertThat(listFactory.getObject() instanceof ArrayList).isTrue();
assertThat(listFactory.getObject()).isInstanceOf(ArrayList.class);
SetFactoryBean setFactory = new SetFactoryBean();
setFactory.setSourceSet(new TreeSet());
setFactory.afterPropertiesSet();
assertThat(setFactory.getObject() instanceof LinkedHashSet).isTrue();
assertThat(setFactory.getObject()).isInstanceOf(LinkedHashSet.class);
MapFactoryBean mapFactory = new MapFactoryBean();
mapFactory.setSourceMap(new TreeMap());
mapFactory.afterPropertiesSet();
assertThat(mapFactory.getObject() instanceof LinkedHashMap).isTrue();
assertThat(mapFactory.getObject()).isInstanceOf(LinkedHashMap.class);
}
@Test
@@ -154,7 +154,7 @@ public class XmlBeanCollectionTests {
TestBean loner = (TestBean) this.beanFactory.getBean("loner");
TestBean dave = (TestBean) this.beanFactory.getBean("david");
assertThat(loner.getFriends().size()).isEqualTo(1);
assertThat(loner.getFriends().contains(dave)).isTrue();
assertThat(loner.getFriends()).contains(dave);
}
@Test
@@ -198,14 +198,14 @@ public class XmlBeanCollectionTests {
public void testMapWithLiteralsAndReferences() {
HasMap hasMap = (HasMap) this.beanFactory.getBean("mixedMap");
assertThat(hasMap.getMap().size()).isEqualTo(5);
assertThat(hasMap.getMap().get("foo").equals(10)).isTrue();
assertThat(hasMap.getMap().get("foo")).isEqualTo(10);
TestBean jenny = (TestBean) this.beanFactory.getBean("jenny");
assertThat(hasMap.getMap().get("jenny")).isSameAs(jenny);
assertThat(hasMap.getMap().get(5).equals("david")).isTrue();
assertThat(hasMap.getMap().get("bar") instanceof Long).isTrue();
assertThat(hasMap.getMap().get("bar").equals(100L)).isTrue();
assertThat(hasMap.getMap().get("baz") instanceof Integer).isTrue();
assertThat(hasMap.getMap().get("baz").equals(200)).isTrue();
assertThat(hasMap.getMap().get("bar")).isInstanceOf(Long.class);
assertThat(hasMap.getMap().get("bar")).isEqualTo(100L);
assertThat(hasMap.getMap().get("baz")).isInstanceOf(Integer.class);
assertThat(hasMap.getMap().get("baz")).isEqualTo(200);
}
@Test
@@ -230,14 +230,14 @@ public class XmlBeanCollectionTests {
assertThat(hasMap.getMap().size()).isEqualTo(4);
assertThat(hasMap.getMap().get(null).equals("bar")).isTrue();
TestBean jenny = (TestBean) this.beanFactory.getBean("jenny");
assertThat(hasMap.getMap().get("jenny").equals(jenny)).isTrue();
assertThat(hasMap.getMap().get("jenny")).isEqualTo(jenny);
// Check list
List l = (List) hasMap.getMap().get("list");
assertThat(l).isNotNull();
assertThat(l.size()).isEqualTo(4);
assertThat(l.get(0).equals("zero")).isTrue();
assertThat(l.get(3)).isNull();
assertThat(l).element(3).isNull();
// Check nested map in list
Map m = (Map) l.get(1);
@@ -250,7 +250,7 @@ public class XmlBeanCollectionTests {
l = (List) l.get(2);
assertThat(l).isNotNull();
assertThat(l.size()).isEqualTo(2);
assertThat(l.get(0).equals(jenny)).isTrue();
assertThat(l.get(0)).isEqualTo(jenny);
assertThat(l.get(1).equals("ba")).isTrue();
// Check nested map
@@ -296,8 +296,8 @@ public class XmlBeanCollectionTests {
HasMap hasMap = (HasMap) this.beanFactory.getBean("identityMap");
assertThat(hasMap.getIdentityMap().size()).isEqualTo(2);
HashSet set = new HashSet(hasMap.getIdentityMap().keySet());
assertThat(set.contains("foo")).isTrue();
assertThat(set.contains("jenny")).isTrue();
assertThat(set).contains("foo");
assertThat(set).contains("jenny");
}
@Test
@@ -364,43 +364,43 @@ public class XmlBeanCollectionTests {
@Test
public void testListFactory() {
List list = (List) this.beanFactory.getBean("listFactory");
assertThat(list instanceof LinkedList).isTrue();
assertThat(list).isInstanceOf(LinkedList.class);
assertThat(list.size()).isEqualTo(2);
assertThat(list.get(0)).isEqualTo("bar");
assertThat(list.get(1)).isEqualTo("jenny");
assertThat(list).element(0).isEqualTo("bar");
assertThat(list).element(1).isEqualTo("jenny");
}
@Test
public void testPrototypeListFactory() {
List list = (List) this.beanFactory.getBean("pListFactory");
assertThat(list instanceof LinkedList).isTrue();
assertThat(list).isInstanceOf(LinkedList.class);
assertThat(list.size()).isEqualTo(2);
assertThat(list.get(0)).isEqualTo("bar");
assertThat(list.get(1)).isEqualTo("jenny");
assertThat(list).element(0).isEqualTo("bar");
assertThat(list).element(1).isEqualTo("jenny");
}
@Test
public void testSetFactory() {
Set set = (Set) this.beanFactory.getBean("setFactory");
assertThat(set instanceof TreeSet).isTrue();
assertThat(set).isInstanceOf(TreeSet.class);
assertThat(set.size()).isEqualTo(2);
assertThat(set.contains("bar")).isTrue();
assertThat(set.contains("jenny")).isTrue();
assertThat(set).contains("bar");
assertThat(set).contains("jenny");
}
@Test
public void testPrototypeSetFactory() {
Set set = (Set) this.beanFactory.getBean("pSetFactory");
assertThat(set instanceof TreeSet).isTrue();
assertThat(set).isInstanceOf(TreeSet.class);
assertThat(set.size()).isEqualTo(2);
assertThat(set.contains("bar")).isTrue();
assertThat(set.contains("jenny")).isTrue();
assertThat(set).contains("bar");
assertThat(set).contains("jenny");
}
@Test
public void testMapFactory() {
Map map = (Map) this.beanFactory.getBean("mapFactory");
assertThat(map instanceof TreeMap).isTrue();
assertThat(map).isInstanceOf(TreeMap.class);
assertThat(map.size()).isEqualTo(2);
assertThat(map.get("foo")).isEqualTo("bar");
assertThat(map.get("jen")).isEqualTo("jenny");
@@ -409,7 +409,7 @@ public class XmlBeanCollectionTests {
@Test
public void testPrototypeMapFactory() {
Map map = (Map) this.beanFactory.getBean("pMapFactory");
assertThat(map instanceof TreeMap).isTrue();
assertThat(map).isInstanceOf(TreeMap.class);
assertThat(map.size()).isEqualTo(2);
assertThat(map.get("foo")).isEqualTo("bar");
assertThat(map.get("jen")).isEqualTo("jenny");
@@ -430,8 +430,8 @@ public class XmlBeanCollectionTests {
public void testEnumSetFactory() {
Set set = (Set) this.beanFactory.getBean("enumSetFactory");
assertThat(set.size()).isEqualTo(2);
assertThat(set.contains("ONE")).isTrue();
assertThat(set.contains("TWO")).isTrue();
assertThat(set).contains("ONE");
assertThat(set).contains("TWO");
}
@@ -59,12 +59,12 @@ public class CustomCollectionEditorTests {
editor.setValue(new int[] {0, 1, 2});
Object value = editor.getValue();
assertThat(value).isNotNull();
assertThat(value instanceof ArrayList).isTrue();
assertThat(value).isInstanceOf(ArrayList.class);
List<?> list = (List<?>) value;
assertThat(list).as("There must be 3 elements in the converted collection").hasSize(3);
assertThat(list.get(0)).isEqualTo(0);
assertThat(list.get(1)).isEqualTo(1);
assertThat(list.get(2)).isEqualTo(2);
assertThat(list).element(0).isEqualTo(0);
assertThat(list).element(1).isEqualTo(1);
assertThat(list).element(2).isEqualTo(2);
}
@Test
@@ -74,7 +74,7 @@ public class CustomCollectionEditorTests {
Collection<?> value = (Collection<?>) editor.getValue();
assertThat(value).isNotNull();
assertThat(value).as("There must be 1 element in the converted collection").hasSize(1);
assertThat(value.iterator().next()).isEqualTo("0, 1, 2");
assertThat(value).element(0).isEqualTo("0, 1, 2");
}
@Test
@@ -83,10 +83,10 @@ public class CustomCollectionEditorTests {
editor.setValue("0, 1, 2");
Object value = editor.getValue();
assertThat(value).isNotNull();
assertThat(value instanceof ArrayList).isTrue();
assertThat(value).isInstanceOf(ArrayList.class);
List<?> list = (List<?>) value;
assertThat(list).as("There must be 1 element in the converted collection").hasSize(1);
assertThat(list.get(0)).isEqualTo("0, 1, 2");
assertThat(list).element(0).isEqualTo("0, 1, 2");
}
@@ -67,7 +67,7 @@ import static org.assertj.core.api.Assertions.within;
class CustomEditorTests {
@Test
void testComplexObject() {
void complexObject() {
TestBean tb = new TestBean();
String newName = "Rod";
String tbString = "Kerry_34";
@@ -85,7 +85,7 @@ class CustomEditorTests {
}
@Test
void testComplexObjectWithOldValueAccess() {
void complexObjectWithOldValueAccess() {
TestBean tb = new TestBean();
String newName = "Rod";
String tbString = "Kerry_34";
@@ -109,7 +109,7 @@ class CustomEditorTests {
}
@Test
void testCustomEditorForSingleProperty() {
void customEditorForSingleProperty() {
TestBean tb = new TestBean();
BeanWrapper bw = new BeanWrapperImpl(tb);
bw.registerCustomEditor(String.class, "name", new PropertyEditorSupport() {
@@ -127,7 +127,7 @@ class CustomEditorTests {
}
@Test
void testCustomEditorForAllStringProperties() {
void customEditorForAllStringProperties() {
TestBean tb = new TestBean();
BeanWrapper bw = new BeanWrapperImpl(tb);
bw.registerCustomEditor(String.class, new PropertyEditorSupport() {
@@ -145,7 +145,7 @@ class CustomEditorTests {
}
@Test
void testCustomEditorForSingleNestedProperty() {
void customEditorForSingleNestedProperty() {
TestBean tb = new TestBean();
tb.setSpouse(new TestBean());
BeanWrapper bw = new BeanWrapperImpl(tb);
@@ -164,7 +164,7 @@ class CustomEditorTests {
}
@Test
void testCustomEditorForAllNestedStringProperties() {
void customEditorForAllNestedStringProperties() {
TestBean tb = new TestBean();
tb.setSpouse(new TestBean());
BeanWrapper bw = new BeanWrapperImpl(tb);
@@ -183,7 +183,7 @@ class CustomEditorTests {
}
@Test
void testDefaultBooleanEditorForPrimitiveType() {
void defaultBooleanEditorForPrimitiveType() {
BooleanTestBean tb = new BooleanTestBean();
BeanWrapper bw = new BeanWrapperImpl(tb);
@@ -193,38 +193,38 @@ class CustomEditorTests {
bw.setPropertyValue("bool1", "false");
assertThat(Boolean.FALSE.equals(bw.getPropertyValue("bool1"))).as("Correct bool1 value").isTrue();
assertThat(!tb.isBool1()).as("Correct bool1 value").isTrue();
assertThat(tb.isBool1()).as("Correct bool1 value").isFalse();
bw.setPropertyValue("bool1", " true ");
assertThat(tb.isBool1()).as("Correct bool1 value").isTrue();
bw.setPropertyValue("bool1", " false ");
assertThat(!tb.isBool1()).as("Correct bool1 value").isTrue();
assertThat(tb.isBool1()).as("Correct bool1 value").isFalse();
bw.setPropertyValue("bool1", "on");
assertThat(tb.isBool1()).as("Correct bool1 value").isTrue();
bw.setPropertyValue("bool1", "off");
assertThat(!tb.isBool1()).as("Correct bool1 value").isTrue();
assertThat(tb.isBool1()).as("Correct bool1 value").isFalse();
bw.setPropertyValue("bool1", "yes");
assertThat(tb.isBool1()).as("Correct bool1 value").isTrue();
bw.setPropertyValue("bool1", "no");
assertThat(!tb.isBool1()).as("Correct bool1 value").isTrue();
assertThat(tb.isBool1()).as("Correct bool1 value").isFalse();
bw.setPropertyValue("bool1", "1");
assertThat(tb.isBool1()).as("Correct bool1 value").isTrue();
bw.setPropertyValue("bool1", "0");
assertThat(!tb.isBool1()).as("Correct bool1 value").isTrue();
assertThat(tb.isBool1()).as("Correct bool1 value").isFalse();
assertThatExceptionOfType(BeansException.class).isThrownBy(() ->
bw.setPropertyValue("bool1", "argh"));
}
@Test
void testDefaultBooleanEditorForWrapperType() {
void defaultBooleanEditorForWrapperType() {
BooleanTestBean tb = new BooleanTestBean();
BeanWrapper bw = new BeanWrapperImpl(tb);
@@ -234,32 +234,32 @@ class CustomEditorTests {
bw.setPropertyValue("bool2", "false");
assertThat(Boolean.FALSE.equals(bw.getPropertyValue("bool2"))).as("Correct bool2 value").isTrue();
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
assertThat(tb.getBool2()).as("Correct bool2 value").isFalse();
bw.setPropertyValue("bool2", "on");
assertThat(tb.getBool2().booleanValue()).as("Correct bool2 value").isTrue();
bw.setPropertyValue("bool2", "off");
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
assertThat(tb.getBool2()).as("Correct bool2 value").isFalse();
bw.setPropertyValue("bool2", "yes");
assertThat(tb.getBool2().booleanValue()).as("Correct bool2 value").isTrue();
bw.setPropertyValue("bool2", "no");
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
assertThat(tb.getBool2()).as("Correct bool2 value").isFalse();
bw.setPropertyValue("bool2", "1");
assertThat(tb.getBool2().booleanValue()).as("Correct bool2 value").isTrue();
bw.setPropertyValue("bool2", "0");
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
assertThat(tb.getBool2()).as("Correct bool2 value").isFalse();
bw.setPropertyValue("bool2", "");
assertThat(tb.getBool2()).as("Correct bool2 value").isNull();
}
@Test
void testCustomBooleanEditorWithAllowEmpty() {
void customBooleanEditorWithAllowEmpty() {
BooleanTestBean tb = new BooleanTestBean();
BeanWrapper bw = new BeanWrapperImpl(tb);
bw.registerCustomEditor(Boolean.class, new CustomBooleanEditor(true));
@@ -270,25 +270,25 @@ class CustomEditorTests {
bw.setPropertyValue("bool2", "false");
assertThat(Boolean.FALSE.equals(bw.getPropertyValue("bool2"))).as("Correct bool2 value").isTrue();
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
assertThat(tb.getBool2()).as("Correct bool2 value").isFalse();
bw.setPropertyValue("bool2", "on");
assertThat(tb.getBool2().booleanValue()).as("Correct bool2 value").isTrue();
bw.setPropertyValue("bool2", "off");
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
assertThat(tb.getBool2()).as("Correct bool2 value").isFalse();
bw.setPropertyValue("bool2", "yes");
assertThat(tb.getBool2().booleanValue()).as("Correct bool2 value").isTrue();
bw.setPropertyValue("bool2", "no");
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
assertThat(tb.getBool2()).as("Correct bool2 value").isFalse();
bw.setPropertyValue("bool2", "1");
assertThat(tb.getBool2().booleanValue()).as("Correct bool2 value").isTrue();
bw.setPropertyValue("bool2", "0");
assertThat(!tb.getBool2()).as("Correct bool2 value").isTrue();
assertThat(tb.getBool2()).as("Correct bool2 value").isFalse();
bw.setPropertyValue("bool2", "");
assertThat(bw.getPropertyValue("bool2")).as("Correct bool2 value").isNull();
@@ -296,7 +296,7 @@ class CustomEditorTests {
}
@Test
void testCustomBooleanEditorWithSpecialTrueAndFalseStrings() {
void customBooleanEditorWithSpecialTrueAndFalseStrings() {
String trueString = "pechorin";
String falseString = "nash";
@@ -320,7 +320,7 @@ class CustomEditorTests {
}
@Test
void testDefaultNumberEditor() {
void defaultNumberEditor() {
NumberTestBean tb = new NumberTestBean();
BeanWrapper bw = new BeanWrapperImpl(tb);
@@ -364,7 +364,7 @@ class CustomEditorTests {
}
@Test
void testCustomNumberEditorWithoutAllowEmpty() {
void customNumberEditorWithoutAllowEmpty() {
NumberFormat nf = NumberFormat.getNumberInstance(Locale.GERMAN);
NumberTestBean tb = new NumberTestBean();
BeanWrapper bw = new BeanWrapperImpl(tb);
@@ -421,7 +421,7 @@ class CustomEditorTests {
}
@Test
void testCustomNumberEditorWithAllowEmpty() {
void customNumberEditorWithAllowEmpty() {
NumberFormat nf = NumberFormat.getNumberInstance(Locale.GERMAN);
NumberTestBean tb = new NumberTestBean();
BeanWrapper bw = new BeanWrapperImpl(tb);
@@ -445,7 +445,7 @@ class CustomEditorTests {
}
@Test
void testCustomNumberEditorWithFrenchBigDecimal() {
void customNumberEditorWithFrenchBigDecimal() {
NumberFormat nf = NumberFormat.getNumberInstance(Locale.FRENCH);
NumberTestBean tb = new NumberTestBean();
BeanWrapper bw = new BeanWrapperImpl(tb);
@@ -462,14 +462,14 @@ class CustomEditorTests {
}
@Test
void testParseShortGreaterThanMaxValueWithoutNumberFormat() {
void parseShortGreaterThanMaxValueWithoutNumberFormat() {
CustomNumberEditor editor = new CustomNumberEditor(Short.class, true);
assertThatExceptionOfType(NumberFormatException.class).as("greater than Short.MAX_VALUE + 1").isThrownBy(() ->
editor.setAsText(String.valueOf(Short.MAX_VALUE + 1)));
}
@Test
void testByteArrayPropertyEditor() {
void byteArrayPropertyEditor() {
PrimitiveArrayBean bean = new PrimitiveArrayBean();
BeanWrapper bw = new BeanWrapperImpl(bean);
bw.setPropertyValue("byteArray", "myvalue");
@@ -477,7 +477,7 @@ class CustomEditorTests {
}
@Test
void testCharArrayPropertyEditor() {
void charArrayPropertyEditor() {
PrimitiveArrayBean bean = new PrimitiveArrayBean();
BeanWrapper bw = new BeanWrapperImpl(bean);
bw.setPropertyValue("charArray", "myvalue");
@@ -485,7 +485,7 @@ class CustomEditorTests {
}
@Test
void testCharacterEditor() {
void characterEditor() {
CharBean cb = new CharBean();
BeanWrapper bw = new BeanWrapperImpl(cb);
@@ -507,7 +507,7 @@ class CustomEditorTests {
}
@Test
void testCharacterEditorWithAllowEmpty() {
void characterEditorWithAllowEmpty() {
CharBean cb = new CharBean();
BeanWrapper bw = new BeanWrapperImpl(cb);
bw.registerCustomEditor(Character.class, new CharacterEditor(true));
@@ -529,14 +529,14 @@ class CustomEditorTests {
}
@Test
void testCharacterEditorSetAsTextWithStringLongerThanOneCharacter() {
void characterEditorSetAsTextWithStringLongerThanOneCharacter() {
PropertyEditor charEditor = new CharacterEditor(false);
assertThatIllegalArgumentException().isThrownBy(() ->
charEditor.setAsText("ColdWaterCanyon"));
}
@Test
void testCharacterEditorGetAsTextReturnsEmptyStringIfValueIsNull() {
void characterEditorGetAsTextReturnsEmptyStringIfValueIsNull() {
PropertyEditor charEditor = new CharacterEditor(false);
assertThat(charEditor.getAsText()).isEmpty();
charEditor = new CharacterEditor(true);
@@ -549,14 +549,14 @@ class CustomEditorTests {
}
@Test
void testCharacterEditorSetAsTextWithNullNotAllowingEmptyAsNull() {
void characterEditorSetAsTextWithNullNotAllowingEmptyAsNull() {
PropertyEditor charEditor = new CharacterEditor(false);
assertThatIllegalArgumentException().isThrownBy(() ->
charEditor.setAsText(null));
}
@Test
void testClassEditor() {
void classEditor() {
PropertyEditor classEditor = new ClassEditor();
classEditor.setAsText(TestBean.class.getName());
assertThat(classEditor.getValue()).isEqualTo(TestBean.class);
@@ -571,14 +571,14 @@ class CustomEditorTests {
}
@Test
void testClassEditorWithNonExistentClass() {
void classEditorWithNonExistentClass() {
PropertyEditor classEditor = new ClassEditor();
assertThatIllegalArgumentException().isThrownBy(() ->
classEditor.setAsText("hairdresser.on.Fire"));
}
@Test
void testClassEditorWithArray() {
void classEditorWithArray() {
PropertyEditor classEditor = new ClassEditor();
classEditor.setAsText("org.springframework.beans.testfixture.beans.TestBean[]");
assertThat(classEditor.getValue()).isEqualTo(TestBean[].class);
@@ -589,7 +589,7 @@ class CustomEditorTests {
* SPR_2165 - ClassEditor is inconsistent with multidimensional arrays
*/
@Test
void testGetAsTextWithTwoDimensionalArray() {
void getAsTextWithTwoDimensionalArray() {
String[][] chessboard = new String[8][8];
ClassEditor editor = new ClassEditor();
editor.setValue(chessboard.getClass());
@@ -600,7 +600,7 @@ class CustomEditorTests {
* SPR_2165 - ClassEditor is inconsistent with multidimensional arrays
*/
@Test
void testGetAsTextWithRidiculousMultiDimensionalArray() {
void getAsTextWithRidiculousMultiDimensionalArray() {
String[][][][][] ridiculousChessboard = new String[8][4][0][1][3];
ClassEditor editor = new ClassEditor();
editor.setValue(ridiculousChessboard.getClass());
@@ -608,7 +608,7 @@ class CustomEditorTests {
}
@Test
void testFileEditor() {
void fileEditor() {
PropertyEditor fileEditor = new FileEditor();
fileEditor.setAsText("file:myfile.txt");
assertThat(fileEditor.getValue()).isEqualTo(new File("myfile.txt"));
@@ -616,7 +616,7 @@ class CustomEditorTests {
}
@Test
void testFileEditorWithRelativePath() {
void fileEditorWithRelativePath() {
PropertyEditor fileEditor = new FileEditor();
try {
fileEditor.setAsText("myfile.txt");
@@ -628,7 +628,7 @@ class CustomEditorTests {
}
@Test
void testFileEditorWithAbsolutePath() {
void fileEditorWithAbsolutePath() {
PropertyEditor fileEditor = new FileEditor();
// testing on Windows
if (new File("C:/myfile.txt").isAbsolute()) {
@@ -643,7 +643,7 @@ class CustomEditorTests {
}
@Test
void testLocaleEditor() {
void localeEditor() {
PropertyEditor localeEditor = new LocaleEditor();
localeEditor.setAsText("en_CA");
assertThat(localeEditor.getValue()).isEqualTo(Locale.CANADA);
@@ -654,7 +654,7 @@ class CustomEditorTests {
}
@Test
void testPatternEditor() {
void patternEditor() {
final String REGEX = "a.*";
PropertyEditor patternEditor = new PatternEditor();
@@ -671,7 +671,7 @@ class CustomEditorTests {
}
@Test
void testCustomBooleanEditor() {
void customBooleanEditor() {
CustomBooleanEditor editor = new CustomBooleanEditor(false);
editor.setAsText("true");
@@ -691,7 +691,7 @@ class CustomEditorTests {
}
@Test
void testCustomBooleanEditorWithEmptyAsNull() {
void customBooleanEditorWithEmptyAsNull() {
CustomBooleanEditor editor = new CustomBooleanEditor(true);
editor.setAsText("true");
@@ -708,7 +708,7 @@ class CustomEditorTests {
}
@Test
void testCustomDateEditor() {
void customDateEditor() {
CustomDateEditor editor = new CustomDateEditor(new SimpleDateFormat("MM/dd/yyyy"), false);
editor.setValue(null);
assertThat(editor.getValue()).isNull();
@@ -716,7 +716,7 @@ class CustomEditorTests {
}
@Test
void testCustomDateEditorWithEmptyAsNull() {
void customDateEditorWithEmptyAsNull() {
CustomDateEditor editor = new CustomDateEditor(new SimpleDateFormat("MM/dd/yyyy"), true);
editor.setValue(null);
assertThat(editor.getValue()).isNull();
@@ -724,7 +724,7 @@ class CustomEditorTests {
}
@Test
void testCustomDateEditorWithExactDateLength() {
void customDateEditorWithExactDateLength() {
int maxLength = 10;
String validDate = "01/01/2005";
String invalidDate = "01/01/05";
@@ -740,7 +740,7 @@ class CustomEditorTests {
}
@Test
void testCustomNumberEditor() {
void customNumberEditor() {
CustomNumberEditor editor = new CustomNumberEditor(Integer.class, false);
editor.setAsText("5");
assertThat(editor.getValue()).isEqualTo(5);
@@ -751,14 +751,14 @@ class CustomEditorTests {
}
@Test
void testCustomNumberEditorWithHex() {
void customNumberEditorWithHex() {
CustomNumberEditor editor = new CustomNumberEditor(Integer.class, false);
editor.setAsText("0x" + Integer.toHexString(64));
assertThat(editor.getValue()).isEqualTo(64);
}
@Test
void testCustomNumberEditorWithEmptyAsNull() {
void customNumberEditorWithEmptyAsNull() {
CustomNumberEditor editor = new CustomNumberEditor(Integer.class, true);
editor.setAsText("5");
assertThat(editor.getValue()).isEqualTo(5);
@@ -772,7 +772,7 @@ class CustomEditorTests {
}
@Test
void testStringTrimmerEditor() {
void stringTrimmerEditor() {
StringTrimmerEditor editor = new StringTrimmerEditor(false);
editor.setAsText("test");
assertThat(editor.getValue()).isEqualTo("test");
@@ -790,7 +790,7 @@ class CustomEditorTests {
}
@Test
void testStringTrimmerEditorWithEmptyAsNull() {
void stringTrimmerEditorWithEmptyAsNull() {
StringTrimmerEditor editor = new StringTrimmerEditor(true);
editor.setAsText("test");
assertThat(editor.getValue()).isEqualTo("test");
@@ -806,7 +806,7 @@ class CustomEditorTests {
}
@Test
void testStringTrimmerEditorWithCharsToDelete() {
void stringTrimmerEditorWithCharsToDelete() {
StringTrimmerEditor editor = new StringTrimmerEditor("\r\n\f", false);
editor.setAsText("te\ns\ft");
assertThat(editor.getValue()).isEqualTo("test");
@@ -822,7 +822,7 @@ class CustomEditorTests {
}
@Test
void testStringTrimmerEditorWithCharsToDeleteAndEmptyAsNull() {
void stringTrimmerEditorWithCharsToDeleteAndEmptyAsNull() {
StringTrimmerEditor editor = new StringTrimmerEditor("\r\n\f", true);
editor.setAsText("te\ns\ft");
assertThat(editor.getValue()).isEqualTo("test");
@@ -838,7 +838,7 @@ class CustomEditorTests {
}
@Test
void testIndexedPropertiesWithCustomEditorForType() {
void indexedPropertiesWithCustomEditorForType() {
IndexedTestBean bean = new IndexedTestBean();
BeanWrapper bw = new BeanWrapperImpl(bean);
bw.registerCustomEditor(String.class, new PropertyEditorSupport() {
@@ -891,7 +891,7 @@ class CustomEditorTests {
}
@Test
void testIndexedPropertiesWithCustomEditorForProperty() {
void indexedPropertiesWithCustomEditorForProperty() {
IndexedTestBean bean = new IndexedTestBean(false);
BeanWrapper bw = new BeanWrapperImpl(bean);
bw.registerCustomEditor(String.class, "array.name", new PropertyEditorSupport() {
@@ -958,7 +958,7 @@ class CustomEditorTests {
}
@Test
void testIndexedPropertiesWithIndividualCustomEditorForProperty() {
void indexedPropertiesWithIndividualCustomEditorForProperty() {
IndexedTestBean bean = new IndexedTestBean(false);
BeanWrapper bw = new BeanWrapperImpl(bean);
bw.registerCustomEditor(String.class, "array[0].name", new PropertyEditorSupport() {
@@ -1043,7 +1043,7 @@ class CustomEditorTests {
}
@Test
void testNestedIndexedPropertiesWithCustomEditorForProperty() {
void nestedIndexedPropertiesWithCustomEditorForProperty() {
IndexedTestBean bean = new IndexedTestBean();
TestBean tb0 = bean.getArray()[0];
TestBean tb1 = bean.getArray()[1];
@@ -1127,7 +1127,7 @@ class CustomEditorTests {
}
@Test
void testNestedIndexedPropertiesWithIndexedCustomEditorForProperty() {
void nestedIndexedPropertiesWithIndexedCustomEditorForProperty() {
IndexedTestBean bean = new IndexedTestBean();
TestBean tb0 = bean.getArray()[0];
TestBean tb1 = bean.getArray()[1];
@@ -1178,7 +1178,7 @@ class CustomEditorTests {
}
@Test
void testIndexedPropertiesWithDirectAccessAndPropertyEditors() {
void indexedPropertiesWithDirectAccessAndPropertyEditors() {
IndexedTestBean bean = new IndexedTestBean();
BeanWrapper bw = new BeanWrapperImpl(bean);
bw.registerCustomEditor(TestBean.class, "array", new PropertyEditorSupport() {
@@ -1232,7 +1232,7 @@ class CustomEditorTests {
}
@Test
void testIndexedPropertiesWithDirectAccessAndSpecificPropertyEditors() {
void indexedPropertiesWithDirectAccessAndSpecificPropertyEditors() {
IndexedTestBean bean = new IndexedTestBean();
BeanWrapper bw = new BeanWrapperImpl(bean);
bw.registerCustomEditor(TestBean.class, "array[0]", new PropertyEditorSupport() {
@@ -1319,7 +1319,8 @@ class CustomEditorTests {
}
@Test
void testIndexedPropertiesWithListPropertyEditor() {
@SuppressWarnings("unchecked")
void indexedPropertiesWithListPropertyEditor() {
IndexedTestBean bean = new IndexedTestBean();
BeanWrapper bw = new BeanWrapperImpl(bean);
bw.registerCustomEditor(List.class, "list", new PropertyEditorSupport() {
@@ -1333,11 +1334,11 @@ class CustomEditorTests {
bw.setPropertyValue("list", "1");
assertThat(((TestBean) bean.getList().get(0)).getName()).isEqualTo("list1");
bw.setPropertyValue("list[0]", "test");
assertThat(bean.getList().get(0)).isEqualTo("test");
assertThat(bean.getList()).element(0).isEqualTo("test");
}
@Test
void testConversionToOldCollections() throws PropertyVetoException {
void conversionToOldCollections() throws PropertyVetoException {
OldCollectionsBean tb = new OldCollectionsBean();
BeanWrapper bw = new BeanWrapperImpl(tb);
bw.registerCustomEditor(Vector.class, new CustomCollectionEditor(Vector.class));
@@ -1345,8 +1346,8 @@ class CustomEditorTests {
bw.setPropertyValue("vector", new String[] {"a", "b"});
assertThat(tb.getVector()).hasSize(2);
assertThat(tb.getVector().get(0)).isEqualTo("a");
assertThat(tb.getVector().get(1)).isEqualTo("b");
assertThat(tb.getVector()).element(0).isEqualTo("a");
assertThat(tb.getVector()).element(1).isEqualTo("b");
bw.setPropertyValue("hashtable", Collections.singletonMap("foo", "bar"));
assertThat(tb.getHashtable()).hasSize(1);
@@ -1354,7 +1355,8 @@ class CustomEditorTests {
}
@Test
void testUninitializedArrayPropertyWithCustomEditor() {
@SuppressWarnings("unchecked")
void uninitializedArrayPropertyWithCustomEditor() {
IndexedTestBean bean = new IndexedTestBean(false);
BeanWrapper bw = new BeanWrapperImpl(bean);
PropertyEditor pe = new CustomNumberEditor(Integer.class, true);
@@ -1362,7 +1364,7 @@ class CustomEditorTests {
TestBean tb = new TestBean();
bw.setPropertyValue("list", new ArrayList<>());
bw.setPropertyValue("list[0]", tb);
assertThat(bean.getList().get(0)).isEqualTo(tb);
assertThat(bean.getList()).element(0).isEqualTo(tb);
assertThat(bw.findCustomEditor(int.class, "list.age")).isEqualTo(pe);
assertThat(bw.findCustomEditor(null, "list.age")).isEqualTo(pe);
assertThat(bw.findCustomEditor(int.class, "list[0].age")).isEqualTo(pe);
@@ -1370,7 +1372,7 @@ class CustomEditorTests {
}
@Test
void testArrayToArrayConversion() throws PropertyVetoException {
void arrayToArrayConversion() throws PropertyVetoException {
IndexedTestBean tb = new IndexedTestBean();
BeanWrapper bw = new BeanWrapperImpl(tb);
bw.registerCustomEditor(TestBean.class, new PropertyEditorSupport() {
@@ -1386,7 +1388,7 @@ class CustomEditorTests {
}
@Test
void testArrayToStringConversion() throws PropertyVetoException {
void arrayToStringConversion() throws PropertyVetoException {
TestBean tb = new TestBean();
BeanWrapper bw = new BeanWrapperImpl(tb);
bw.registerCustomEditor(String.class, new PropertyEditorSupport() {
@@ -1400,7 +1402,7 @@ class CustomEditorTests {
}
@Test
void testClassArrayEditorSunnyDay() {
void classArrayEditorSunnyDay() {
ClassArrayEditor classArrayEditor = new ClassArrayEditor();
classArrayEditor.setAsText("java.lang.String,java.util.HashMap");
Class<?>[] classes = (Class<?>[]) classArrayEditor.getValue();
@@ -1413,7 +1415,7 @@ class CustomEditorTests {
}
@Test
void testClassArrayEditorSunnyDayWithArrayTypes() {
void classArrayEditorSunnyDayWithArrayTypes() {
ClassArrayEditor classArrayEditor = new ClassArrayEditor();
classArrayEditor.setAsText("java.lang.String[],java.util.Map[],int[],float[][][]");
Class<?>[] classes = (Class<?>[]) classArrayEditor.getValue();
@@ -1428,7 +1430,7 @@ class CustomEditorTests {
}
@Test
void testClassArrayEditorSetAsTextWithNull() {
void classArrayEditorSetAsTextWithNull() {
ClassArrayEditor editor = new ClassArrayEditor();
editor.setAsText(null);
assertThat(editor.getValue()).isNull();
@@ -1436,7 +1438,7 @@ class CustomEditorTests {
}
@Test
void testClassArrayEditorSetAsTextWithEmptyString() {
void classArrayEditorSetAsTextWithEmptyString() {
ClassArrayEditor editor = new ClassArrayEditor();
editor.setAsText("");
assertThat(editor.getValue()).isNull();
@@ -1444,7 +1446,7 @@ class CustomEditorTests {
}
@Test
void testClassArrayEditorSetAsTextWithWhitespaceString() {
void classArrayEditorSetAsTextWithWhitespaceString() {
ClassArrayEditor editor = new ClassArrayEditor();
editor.setAsText("\n");
assertThat(editor.getValue()).isNull();
@@ -1452,7 +1454,7 @@ class CustomEditorTests {
}
@Test
void testCharsetEditor() {
void charsetEditor() {
CharsetEditor editor = new CharsetEditor();
String name = "UTF-8";
editor.setAsText(name);
@@ -39,7 +39,7 @@ public class FileEditorTests {
fileEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
ClassUtils.getShortName(getClass()) + ".class");
Object value = fileEditor.getValue();
assertThat(value instanceof File).isTrue();
assertThat(value).isInstanceOf(File.class);
File file = (File) value;
assertThat(file).exists();
}
@@ -56,7 +56,7 @@ public class FileEditorTests {
PropertyEditor fileEditor = new FileEditor();
fileEditor.setAsText("file:no_way_this_file_is_found.doc");
Object value = fileEditor.getValue();
assertThat(value instanceof File).isTrue();
assertThat(value).isInstanceOf(File.class);
File file = (File) value;
assertThat(file).doesNotExist();
}
@@ -66,7 +66,7 @@ public class FileEditorTests {
PropertyEditor fileEditor = new FileEditor();
fileEditor.setAsText("/no_way_this_file_is_found.doc");
Object value = fileEditor.getValue();
assertThat(value instanceof File).isTrue();
assertThat(value).isInstanceOf(File.class);
File file = (File) value;
assertThat(file).doesNotExist();
}
@@ -78,7 +78,7 @@ public class FileEditorTests {
ClassUtils.getShortName(getClass()) + ".class";
fileEditor.setAsText(fileName);
Object value = fileEditor.getValue();
assertThat(value instanceof File).isTrue();
assertThat(value).isInstanceOf(File.class);
File file = (File) value;
assertThat(file).exists();
String absolutePath = file.getAbsolutePath().replace('\\', '/');
@@ -92,7 +92,7 @@ public class FileEditorTests {
ClassUtils.getShortName(getClass()) + ".clazz";
fileEditor.setAsText(fileName);
Object value = fileEditor.getValue();
assertThat(value instanceof File).isTrue();
assertThat(value).isInstanceOf(File.class);
File file = (File) value;
assertThat(file).doesNotExist();
String absolutePath = file.getAbsolutePath().replace('\\', '/');

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