Compare commits

..

121 Commits

Author SHA1 Message Date
Spring Builds 0abadaa2f6 Release v6.0.10 2023-06-15 08:19:28 +00:00
Juergen Hoeller b9221656cc Declare ClassLoader for DeserializingConverter constructor as nullable
Closes gh-30670
2023-06-14 22:27:07 +02:00
Juergen Hoeller c30f6aa427 Polishing 2023-06-14 22:17:39 +02:00
Sam Brannen aa2028127f Overhaul TestExecutionListener for Micrometer ObservationRegistry
This commit overhauls the TestExecutionListener for Micrometer's
ObservationRegistry that was introduced in the previous commit.

Specifically, this commit:

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

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

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

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

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

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

- Introduces DEBUG logging for diagnostics.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Cannot handle (252) 'ü'

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Closes gh-30476
2023-05-12 08:49:22 +02:00
Stephane Nicoll a9b94241af Fix typo
Closes gh-30473
2023-05-11 16:35:20 +02:00
Spring Builds 7066dc4ba7 Next development version (v6.0.10-SNAPSHOT) 2023-05-11 12:47:00 +00:00
245 changed files with 3004 additions and 1899 deletions
+8 -13
View File
@@ -4,8 +4,6 @@ plugins {
// kotlinVersion is managed in gradle.properties
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
id 'org.jetbrains.dokka' version '1.8.10'
id 'org.asciidoctor.jvm.convert' version '3.3.2' apply false
id 'org.asciidoctor.jvm.pdf' version '3.3.2' apply false
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
id 'com.github.ben-manes.versions' version '0.46.0'
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
@@ -28,7 +26,6 @@ configure(allprojects) { project ->
includeGroup 'io.projectreactor.netty'
}
}
maven { url "https://repo.spring.io/libs-spring-framework-build" }
if (version.contains('-')) {
maven { url "https://repo.spring.io/milestone" }
}
@@ -78,7 +75,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "10.10.0"
toolVersion = "10.12.0"
configDirectory.set(rootProject.file("src/checkstyle"))
}
@@ -115,10 +112,10 @@ configure([rootProject] + javaProjects) { project ->
ext.javadocLinks = [
"https://docs.oracle.com/en/java/javase/17/docs/api/",
"https://jakarta.ee/specifications/platform/9/apidocs/",
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ
"https://www.ibm.com/docs/api/v1/content/SSEQTP_8.5.5/com.ibm.websphere.javadoc.doc/web/apidocs/",
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/",
"https://docs.jboss.org/jbossas/javadoc/7.1.2.Final/",
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ and weblogic.* packages
"https://www.ibm.com/docs/api/v1/content/SSEQTP_8.5.5/com.ibm.websphere.javadoc.doc/web/apidocs/", // com.ibm.*
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/", // org.jboss.resource.*
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
"https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.14.1/",
@@ -134,11 +131,9 @@ configure([rootProject] + javaProjects) { project ->
"https://www.reactive-streams.org/reactive-streams-1.0.3-javadoc/",
"https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/",
"https://r2dbc.io/spec/1.0.0.RELEASE/api/",
// The external Javadoc link for JSR 305 must come last to ensure that types from
// JSR 250 (such as @PostConstruct) are still supported. This is due to the fact
// that JSR 250 and JSR 305 both define types in javax.annotation, which results
// in a split package, and the javadoc tool does not support split packages
// across multiple external Javadoc sites.
// Previously there could be a split-package issue between JSR250 and JSR305 javax.annotation packages,
// but since 6.0 JSR 250 annotations such as @Resource and @PostConstruct have been replaced by their
// JakartaEE equivalents in the jakarta.annotation package.
"https://www.javadoc.io/doc/com.google.code.findbugs/jsr305/3.0.2/"
] as String[]
}
-47
View File
@@ -64,12 +64,6 @@ resource_types:
<<: *docker-resource-source
repository: dpb587/github-status-resource
tag: master
- name: pull-request
type: registry-image
source:
<<: *docker-resource-source
repository: teliaoss/github-pr-resource
tag: v0.23.0
- name: slack-notification
type: registry-image
source:
@@ -111,14 +105,6 @@ resources:
username: ((artifactory-username))
password: ((artifactory-password))
build_name: ((build-name))
- name: git-pull-request
type: pull-request
icon: source-pull
source:
access_token: ((github-ci-pull-request-token))
repository: ((github-repo-name))
base_branch: ((branch))
ignore_paths: ["ci/*"]
- name: repo-status-build
type: github-status-resource
icon: eye-check-outline
@@ -259,37 +245,6 @@ jobs:
<<: *slack-fail-params
- put: repo-status-jdk20-build
params: { state: "success", commit: "git-repo" }
- name: build-pull-requests
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
resource: git-pull-request
trigger: true
version: every
- do:
- put: git-pull-request
params:
path: git-repo
status: pending
- task: build-pr
image: ci-image
file: git-repo/ci/tasks/build-pr.yml
privileged: true
timeout: ((task-timeout))
params:
BRANCH: ((branch))
on_success:
put: git-pull-request
params:
path: git-repo
status: success
on_failure:
put: git-pull-request
params:
path: git-repo
status: failure
- name: stage-milestone
serial: true
plan:
@@ -446,5 +401,3 @@ groups:
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
jobs: ["build-ci-images"]
- name: "pull-requests"
jobs: [ "build-pull-requests" ]
+1 -1
View File
@@ -5,5 +5,5 @@ source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK20 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check antora
popd > /dev/null
+8 -6
View File
@@ -11,7 +11,7 @@ apply from: "${rootDir}/gradle/publications.gradle"
antora {
version = '3.2.0-alpha.2'
playbook = layout.buildDirectory.file('cached-antora-playbook.yml').get().getAsFile()
playbook = 'cached-antora-playbook.yml'
playbookProvider {
repository = 'spring-projects/spring-framework'
branch = 'docs-build'
@@ -45,9 +45,11 @@ tasks.create("generateAntoraResources") {
dependsOn 'generateAntoraYml'
}
tasks.named("check") {
dependsOn 'antora'
}
// Commented out for now:
// https://github.com/spring-projects/spring-framework/issues/30481
// tasks.named("check") {
// dependsOn 'antora'
// }
jar {
enabled = false
@@ -102,7 +104,7 @@ task api(type: Javadoc) {
overview = "framework-docs/src/docs/api/overview.html"
splitIndex = true
links(project.ext.javadocLinks)
addBooleanOption('Xdoclint:syntax', true) // only check syntax with doclint
addBooleanOption('Xdoclint:syntax,reference', true) // only check syntax and reference with doclint
addBooleanOption('Werror', true) // fail build on Javadoc warnings
}
source moduleProjects.collect { project ->
@@ -227,4 +229,4 @@ publishing {
artifact distZip
}
}
}
}
@@ -25,7 +25,7 @@ The following table lists all currently supported Spring properties.
| `spring.beaninfo.ignore`
| Instructs Spring to use the `Introspector.IGNORE_ALL_BEANINFO` mode when calling the
JavaBeans `Introspector`. See
{api-spring-framework}++/beans/CachedIntrospectionResults.html#IGNORE_BEANINFO_PROPERTY_NAME++[`CachedIntrospectionResults`]
{api-spring-framework}++/beans/StandardBeanInfoFactory.html#IGNORE_BEANINFO_PROPERTY_NAME++[`CachedIntrospectionResults`]
for details.
| `spring.expression.compiler.mode`
@@ -38,7 +38,6 @@ The expression language supports the following functionality:
* Class expressions
* Accessing properties, arrays, lists, and maps
* Method invocation
* Relational operators
* Assignment
* Calling constructors
* Bean references
@@ -2,13 +2,13 @@
= Null-safety
Although Java does not let you express null-safety with its type system, the Spring Framework
now provides the following annotations in the `org.springframework.lang` package to let you
provides the following annotations in the `org.springframework.lang` package to let you
declare nullability of APIs and fields:
* {api-spring-framework}/lang/Nullable.html[`@Nullable`]: Annotation to indicate that a
specific parameter, return value, or field can be `null`.
* {api-spring-framework}/lang/NonNull.html[`@NonNull`]: Annotation to indicate that a specific
parameter, return value, or field cannot be `null` (not needed on parameters / return values
parameter, return value, or field cannot be `null` (not needed on parameters, return values,
and fields where `@NonNullApi` and `@NonNullFields` apply, respectively).
* {api-spring-framework}/lang/NonNullApi.html[`@NonNullApi`]: Annotation at the package level
that declares non-null as the default semantics for parameters and return values.
@@ -17,11 +17,10 @@ level that declares non-null as the default semantics for fields.
The Spring Framework itself leverages these annotations, but they can also be used in any
Spring-based Java project to declare null-safe APIs and optionally null-safe fields.
Generic type arguments, varargs and array elements nullability are not supported yet but
should be in an upcoming release, see https://jira.spring.io/browse/SPR-15942[SPR-15942]
for up-to-date information. Nullability declarations are expected to be fine-tuned between
Spring Framework releases, including minor ones. Nullability of types used inside method
bodies is outside of the scope of this feature.
Nullability declarations for generic type arguments, varargs, and array elements are not supported yet.
Nullability declarations are expected to be fine-tuned between Spring Framework releases,
including minor ones. Nullability of types used inside method bodies is outside the
scope of this feature.
NOTE: Other common libraries such as Reactor and Spring Data provide null-safe APIs that
use a similar nullability arrangement, delivering a consistent overall experience for
@@ -37,8 +36,8 @@ In addition to providing an explicit declaration for Spring Framework API nullab
these annotations can be used by an IDE (such as IDEA or Eclipse) to provide useful
warnings related to null-safety in order to avoid `NullPointerException` at runtime.
They are also used to make Spring API null-safe in Kotlin projects, since Kotlin natively
supports https://kotlinlang.org/docs/reference/null-safety.html[null-safety]. More details
They are also used to make Spring APIs null-safe in Kotlin projects, since Kotlin natively
supports https://kotlinlang.org/docs/null-safety.html[null-safety]. More details
are available in the xref:languages/kotlin/null-safety.adoc[Kotlin support documentation].
@@ -48,11 +47,11 @@ are available in the xref:languages/kotlin/null-safety.adoc[Kotlin support docum
== JSR-305 meta-annotations
Spring annotations are meta-annotated with https://jcp.org/en/jsr/detail?id=305[JSR 305]
annotations (a dormant but wide-spread JSR). JSR-305 meta-annotations let tooling vendors
annotations (a dormant but widespread JSR). JSR-305 meta-annotations let tooling vendors
like IDEA or Kotlin provide null-safety support in a generic way, without having to
hard-code support for Spring annotations.
It is not necessary nor recommended to add a JSR-305 dependency to the project classpath to
take advantage of Spring null-safe API. Only projects such as Spring-based libraries that use
It is neither necessary nor recommended to add a JSR-305 dependency to the project classpath to
take advantage of Spring's null-safe APIs. Only projects such as Spring-based libraries that use
null-safety annotations in their codebase should add `com.google.code.findbugs:jsr305:3.0.2`
with `compileOnly` Gradle configuration or Maven `provided` scope to avoid compile warnings.
with `compileOnly` Gradle configuration or Maven `provided` scope to avoid compiler warnings.
@@ -8,10 +8,16 @@ implementations and transaction demarcation. Most of these patterns can be direc
translated to all other supported ORM tools. The later sections in this chapter then
cover the other ORM technologies and show brief examples.
NOTE: As of Spring Framework 5.3, Spring requires Hibernate ORM 5.2+ for Spring's
[NOTE]
====
As of Spring Framework 6.0, Spring requires Hibernate ORM 5.5+ for Spring's
`HibernateJpaVendorAdapter` as well as for a native Hibernate `SessionFactory` setup.
It is strongly recommended to go with Hibernate ORM 5.4 for a newly started application.
For use with `HibernateJpaVendorAdapter`, Hibernate Search needs to be upgraded to 5.11.6.
We recommend Hibernate ORM 5.6 as the last feature branch in that Hibernate generation.
Hibernate ORM 6.x is only supported as a JPA provider (`HibernateJpaVendorAdapter`).
Plain `SessionFactory` setup with the `orm.hibernate5` package is not supported anymore.
We recommend Hibernate ORM 6.1/6.2 with JPA-style setup for new development projects.
====
[[orm-session-factory-setup]]
@@ -417,9 +417,9 @@ Do not pass `Collection<String>` or the like as an array parameter.
[[r2dbc-DatabaseClient-filter]]
==== Statement Filters
Sometimes it you need to fine-tune options on the actual `Statement`
before it gets run. Register a `Statement` filter
(`StatementFilterFunction`) through `DatabaseClient` to intercept and
Sometimes you need to fine-tune options on the actual `Statement`
before it gets run. To do so, register a `Statement` filter
(`StatementFilterFunction`) with the `DatabaseClient` to intercept and
modify statements in their execution, as the following example shows:
[tabs]
@@ -445,7 +445,8 @@ Kotlin::
----
======
`DatabaseClient` exposes also simplified `filter(…)` overload accepting `Function<Statement, Statement>`:
`DatabaseClient` also exposes a simplified `filter(…)` overload that accepts
a `Function<Statement, Statement>`:
[tabs]
======
@@ -4,10 +4,10 @@
For more information about the Spring Framework's transaction support, see:
* https://www.infoworld.com/article/2077963/distributed-transactions-in-spring--with-and-without-xa.html[Distributed
transactions in Spring, with and without XA] is a JavaWorld presentation in which
Spring's David Syer guides you through seven patterns for distributed
transactions in Spring applications, three of them with XA and four without.
* link:++https://www.infoworld.com/article/2077963/distributed-transactions-in-spring--with-and-without-xa.html++[
Distributed transactions in Spring, with and without XA] is a JavaWorld presentation in
which Spring's David Syer guides you through seven patterns for distributed transactions
in Spring applications, three of them with XA and four without.
* https://www.infoq.com/minibooks/JTDS[_Java Transaction Design Strategies_] is a book
available from https://www.infoq.com/[InfoQ] that provides a well-paced introduction
to transactions in Java. It also includes side-by-side examples of how to configure
@@ -3,7 +3,7 @@
The Spring Framework provides the following choices for making calls to REST endpoints:
* xref:integration/rest-clients.adoc#rest-webclient[`WebClient`] - non-blocking, reactive client w fluent API.
* xref:integration/rest-clients.adoc#rest-webclient[`WebClient`] - non-blocking, reactive client with fluent API.
* xref:integration/rest-clients.adoc#rest-resttemplate[`RestTemplate`] - synchronous client with template method API.
* xref:integration/rest-clients.adoc#rest-http-interface[HTTP Interface] - annotated interface with generated, dynamic proxy implementation.
@@ -12,7 +12,11 @@ metadata.
You can use `@DirtiesContext` as both a class-level and a method-level annotation within
the same class or class hierarchy. In such scenarios, the `ApplicationContext` is marked
as dirty before or after any such annotated method as well as before or after the current
test class, depending on the configured `methodMode` and `classMode`.
test class, depending on the configured `methodMode` and `classMode`. When
`@DirtiesContext` is declared at both the class level and the method level, the
configured modes from both annotations will be honored. For example, if the class mode is
set to `BEFORE_EACH_TEST_METHOD` and the method mode is set to `AFTER_METHOD`, the
context will be marked as dirty both before and after the given test method.
The following examples explain when the context would be dirtied for various
configuration scenarios:
@@ -12,6 +12,8 @@ by default, exactly in the following order:
xref:testing/testcontext-framework/application-events.adoc[`ApplicationEvents`].
* `DependencyInjectionTestExecutionListener`: Provides dependency injection for the test
instance.
* `MicrometerObservationRegistryTestExecutionListener`: Provides support for
Micrometer's `ObservationRegistry`.
* `DirtiesContextTestExecutionListener`: Handles the `@DirtiesContext` annotation for
"`after`" modes.
* `TransactionalTestExecutionListener`: Provides transactional test execution with
@@ -3,11 +3,18 @@
[.small]#xref:web/webflux/config.adoc#webflux-config-message-codecs[See equivalent in the Reactive stack]#
You can customize `HttpMessageConverter` in Java configuration by overriding
{api-spring-framework}/web/servlet/config/annotation/WebMvcConfigurer.html#configureMessageConverters-java.util.List-[`configureMessageConverters()`]
(to replace the default converters created by Spring MVC) or by overriding
{api-spring-framework}/web/servlet/config/annotation/WebMvcConfigurer.html#extendMessageConverters-java.util.List-[`extendMessageConverters()`]
(to customize the default converters or add additional converters to the default ones).
You can set the `HttpMessageConverter` instances to use in Java configuration,
replacing the ones used by default, by overriding
{api-spring-framework}/web/servlet/config/annotation/WebMvcConfigurer.html#configureMessageConverters-java.util.List-[`configureMessageConverters()`].
You can also customize the list of configured message converters at the end by overriding
{api-spring-framework}/web/servlet/config/annotation/WebMvcConfigurer.html#extendMessageConverters-java.util.List-[`extendMessageConverters()`].
TIP: In a Spring Boot application, the `WebMvcAutoConfiguration` adds any
`HttpMessageConverter` beans it detects, in addition to default converters. Hence, in a
Boot application, prefer to use the
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-config/message-converters.html[HttpMessageConverters]
mechanism. Or alternatively, use `extendMessageConverters` to modify message converters
at the end.
The following example adds XML and Jackson JSON converters with a customized
`ObjectMapper` instead of the default ones:
@@ -3,7 +3,7 @@
STOMP over WebSocket support is available in the `spring-messaging` and
`spring-websocket` modules. Once you have those dependencies, you can expose a STOMP
endpoints, over WebSocket with xref:web/websocket/fallback.adoc[SockJS Fallback], as the following example shows:
endpoint over WebSocket with xref:web/websocket/fallback.adoc[SockJS Fallback], as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@@ -32,7 +32,7 @@ client needs to connect for the WebSocket handshake.
<2> STOMP messages whose destination header begins with `/app` are routed to
`@MessageMapping` methods in `@Controller` classes.
<3> Use the built-in message broker for subscriptions and broadcasting and
route messages whose destination header begins with `/topic `or `/queue` to the broker.
route messages whose destination header begins with `/topic` or `/queue` to the broker.
The following example shows the XML configuration equivalent of the preceding example:
+14 -14
View File
@@ -8,12 +8,12 @@ javaPlatform {
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.14.3"))
api(platform("io.micrometer:micrometer-bom:1.10.7"))
api(platform("io.netty:netty-bom:4.1.92.Final"))
api(platform("io.micrometer:micrometer-bom:1.10.8"))
api(platform("io.netty:netty-bom:4.1.93.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2022.0.7"))
api(platform("io.projectreactor:reactor-bom:2022.0.8"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.11"))
api(platform("org.apache.groovy:groovy-bom:4.0.12"))
api(platform("org.apache.logging.log4j:log4j-bom:2.20.0"))
api(platform("org.eclipse.jetty:jetty-bom:11.0.15"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4"))
@@ -29,7 +29,7 @@ dependencies {
api("com.google.code.findbugs:findbugs:3.0.1")
api("com.google.code.findbugs:jsr305:3.0.2")
api("com.google.code.gson:gson:2.10.1")
api("com.google.protobuf:protobuf-java-util:3.23.0")
api("com.google.protobuf:protobuf-java-util:3.23.2")
api("com.googlecode.protobuf-java-format:protobuf-java-format:1.4")
api("com.h2database:h2:2.1.214")
api("com.jayway.jsonpath:json-path:2.8.0")
@@ -45,8 +45,8 @@ dependencies {
api("com.thoughtworks.xstream:xstream:1.4.20")
api("commons-io:commons-io:2.11.0")
api("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.2")
api("info.picocli:picocli:4.7.3")
api("io.micrometer:context-propagation:1.0.0")
api("info.picocli:picocli:4.7.4")
api("io.micrometer:context-propagation:1.0.3")
api("io.mockk:mockk:1.13.4")
api("io.projectreactor.netty:reactor-netty5-http:2.0.0-M3")
api("io.projectreactor.tools:blockhound:1.0.8.RELEASE")
@@ -55,9 +55,9 @@ dependencies {
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
api("io.reactivex.rxjava3:rxjava:3.1.6")
api("io.smallrye.reactive:mutiny:1.9.0")
api("io.undertow:undertow-core:2.3.5.Final")
api("io.undertow:undertow-servlet:2.3.5.Final")
api("io.undertow:undertow-websockets-jsr:2.3.5.Final")
api("io.undertow:undertow-core:2.3.7.Final")
api("io.undertow:undertow-servlet:2.3.7.Final")
api("io.undertow:undertow-websockets-jsr:2.3.7.Final")
api("io.vavr:vavr:0.10.4")
api("jakarta.activation:jakarta.activation-api:2.0.1")
api("jakarta.annotation:jakarta.annotation-api:2.0.0")
@@ -99,8 +99,8 @@ dependencies {
api("org.apache.httpcomponents.client5:httpclient5:5.2.1")
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.2.1")
api("org.apache.poi:poi-ooxml:5.2.3")
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.8")
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.8")
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.9")
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.9")
api("org.apache.tomcat:tomcat-util:10.1.8")
api("org.apache.tomcat:tomcat-websocket:10.1.8")
api("org.aspectj:aspectjrt:1.9.19")
@@ -128,7 +128,7 @@ dependencies {
api("org.hibernate:hibernate-validator:7.0.5.Final")
api("org.hsqldb:hsqldb:2.7.1")
api("org.javamoney:moneta:1.4.2")
api("org.jruby:jruby:9.4.2.0")
api("org.jruby:jruby:9.4.3.0")
api("org.junit.support:testng-engine:1.0.4")
api("org.mozilla:rhino:1.7.14")
api("org.ogce:xpp3:1.1.6")
@@ -138,7 +138,7 @@ dependencies {
api("org.seleniumhq.selenium:selenium-java:3.141.59")
api("org.skyscreamer:jsonassert:1.5.1")
api("org.slf4j:slf4j-api:2.0.7")
api("org.testng:testng:7.7.1")
api("org.testng:testng:7.8.0")
api("org.webjars:underscorejs:1.8.3")
api("org.webjars:webjars-locator-core:0.52")
api("org.xmlunit:xmlunit-assertj:2.9.1")
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.0.9
version=6.0.10
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
+5 -2
View File
@@ -72,10 +72,13 @@ javadoc {
options.header = project.name
options.use = true
options.links(project.ext.javadocLinks)
options.addStringOption("Xdoclint:none", "-quiet")
// Check for syntax during linting. 'none' doesn't seem to work in suppressing
// all linting warnings all the time (see/link references most notably).
options.addStringOption("Xdoclint:syntax", "-quiet")
// Suppress warnings due to cross-module @see and @link references.
// Note that global 'api' task does display all warnings.
// Note that global 'api' task does display all warnings, and
// checks for 'reference' on top of 'syntax'.
logging.captureStandardError LogLevel.INFO
logging.captureStandardOutput LogLevel.INFO // suppress "## warnings" message
}
@@ -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.
@@ -62,8 +62,11 @@ public interface Advisor {
* Typical Advisor implementations always return {@code true}.
* Use singleton/prototype bean definitions or appropriate programmatic
* proxy creation to ensure that Advisors have the correct lifecycle model.
* <p>As of 6.0.10, the default implementation returns {@code true}.
* @return whether this advice is associated with a particular target instance
*/
boolean isPerInstance();
default boolean isPerInstance() {
return true;
}
}
@@ -302,7 +302,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
private void bindParameterName(int index, String name) {
private void bindParameterName(int index, @Nullable String name) {
this.parameterNameBindings[index] = name;
this.numberOfRemainingUnboundArguments--;
}
@@ -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.
@@ -67,11 +67,6 @@ public class AspectJPointcutAdvisor implements PointcutAdvisor, Ordered {
}
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public Advice getAdvice() {
return this.advice;
@@ -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.
@@ -91,11 +91,6 @@ public class DeclareParentsAdvisor implements IntroductionAdvisor {
// Do nothing
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public Advice getAdvice() {
return this.advice;
@@ -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.
@@ -22,6 +22,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -32,6 +33,8 @@ import org.springframework.aop.Advisor;
import org.springframework.aop.DynamicIntroductionAdvice;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.IntroductionInfo;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.TargetSource;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
@@ -41,6 +44,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
/**
* Base class for AOP proxy configuration managers.
@@ -72,15 +76,13 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
/** Package-protected to allow direct access for efficiency. */
@SuppressWarnings("serial")
TargetSource targetSource = EMPTY_TARGET_SOURCE;
/** Whether the Advisors are already filtered for the specific target class. */
private boolean preFiltered = false;
/** The AdvisorChainFactory to use. */
@SuppressWarnings("serial")
AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
private AdvisorChainFactory advisorChainFactory;
/** Cache with Method as key and advisor chain List as value. */
private transient Map<MethodCacheKey, List<Object>> methodCache;
@@ -89,21 +91,22 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
* Interfaces to be implemented by the proxy. Held in List to keep the order
* of registration, to create JDK proxy with specified order of interfaces.
*/
@SuppressWarnings("serial")
private List<Class<?>> interfaces = new ArrayList<>();
/**
* List of Advisors. If an Advice is added, it will be wrapped
* in an Advisor before being added to this List.
*/
@SuppressWarnings("serial")
private List<Advisor> advisors = new ArrayList<>();
private List<Advisor> advisorKey = this.advisors;
/**
* No-arg constructor for use as a JavaBean.
*/
public AdvisedSupport() {
this.advisorChainFactory = DefaultAdvisorChainFactory.INSTANCE;
this.methodCache = new ConcurrentHashMap<>(32);
}
@@ -116,6 +119,15 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
setInterfaces(interfaces);
}
/**
* Internal constructor for {@link #getConfigurationOnlyCopy()}.
* @since 6.0.10
*/
private AdvisedSupport(AdvisorChainFactory advisorChainFactory, Map<MethodCacheKey, List<Object>> methodCache) {
this.advisorChainFactory = advisorChainFactory;
this.methodCache = methodCache;
}
/**
* Set the given object as target.
@@ -520,15 +532,27 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
* replacing the TargetSource.
*/
AdvisedSupport getConfigurationOnlyCopy() {
AdvisedSupport copy = new AdvisedSupport();
AdvisedSupport copy = new AdvisedSupport(this.advisorChainFactory, this.methodCache);
copy.copyFrom(this);
copy.targetSource = EmptyTargetSource.forClass(getTargetClass(), getTargetSource().isStatic());
copy.advisorChainFactory = this.advisorChainFactory;
copy.interfaces = new ArrayList<>(this.interfaces);
copy.advisors = new ArrayList<>(this.advisors);
copy.advisorKey = new ArrayList<>(this.advisors.size());
for (Advisor advisor : this.advisors) {
copy.advisorKey.add(new AdvisorKeyEntry(advisor));
}
return copy;
}
void reduceToAdvisorKey() {
this.advisors = this.advisorKey;
this.methodCache = Collections.emptyMap();
}
Object getAdvisorKey() {
return this.advisorKey;
}
//---------------------------------------------------------------------
// Serialization support
@@ -604,4 +628,51 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
}
}
/**
* Stub for an Advisor instance that is just needed for key purposes,
* allowing for efficient equals and hashCode comparisons against the
* advice class and the pointcut.
* @since 6.0.10
* @see #getConfigurationOnlyCopy()
* @see #getAdvisorKey()
*/
private static class AdvisorKeyEntry implements Advisor {
private final Class<?> adviceType;
@Nullable
private String classFilterKey;
@Nullable
private String methodMatcherKey;
public AdvisorKeyEntry(Advisor advisor) {
this.adviceType = advisor.getAdvice().getClass();
if (advisor instanceof PointcutAdvisor pointcutAdvisor) {
Pointcut pointcut = pointcutAdvisor.getPointcut();
this.classFilterKey = ObjectUtils.identityToString(pointcut.getClassFilter());
this.methodMatcherKey = ObjectUtils.identityToString(pointcut.getMethodMatcher());
}
}
@Override
public Advice getAdvice() {
throw new UnsupportedOperationException();
}
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof AdvisorKeyEntry otherEntry &&
this.adviceType == otherEntry.adviceType &&
ObjectUtils.nullSafeEquals(this.classFilterKey, otherEntry.classFilterKey) &&
ObjectUtils.nullSafeEquals(this.methodMatcherKey, otherEntry.methodMatcherKey)));
}
@Override
public int hashCode() {
return this.adviceType.hashCode();
}
}
}
@@ -26,14 +26,11 @@ import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.Advisor;
import org.springframework.aop.AopInvocationException;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.RawTargetAccess;
import org.springframework.aop.TargetSource;
import org.springframework.aop.support.AopUtils;
@@ -205,12 +202,21 @@ class CglibAopProxy implements AopProxy, Serializable {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
ProxyCallbackFilter filter = new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset);
enhancer.setCallbackFilter(filter);
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));
// ProxyCallbackFilter has method introspection capability with Advisor access.
try {
return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));
}
finally {
// Reduce ProxyCallbackFilter to key-only state for its class cache role
// in the CGLIB$CALLBACK_FILTER field, not leaking any Advisor state...
filter.advised.reduceToAdvisorKey();
}
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
@@ -294,9 +300,9 @@ class CglibAopProxy implements AopProxy, Serializable {
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
// Parameters used for optimization choices...
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();
boolean isFrozen = this.advised.isFrozen();
boolean exposeProxy = this.advised.isExposeProxy();
// Choose an "aop" interceptor (used for AOP calls).
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
@@ -776,7 +782,7 @@ class CglibAopProxy implements AopProxy, Serializable {
*/
private static class ProxyCallbackFilter implements CallbackFilter {
private final AdvisedSupport advised;
final AdvisedSupport advised;
private final Map<Method, Integer> fixedInterceptorMap;
@@ -857,9 +863,9 @@ class CglibAopProxy implements AopProxy, Serializable {
// Proxy is not yet available, but that shouldn't matter.
List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
boolean haveAdvice = !chain.isEmpty();
boolean exposeProxy = this.advised.isExposeProxy();
boolean isStatic = this.advised.getTargetSource().isStatic();
boolean isFrozen = this.advised.isFrozen();
boolean exposeProxy = this.advised.isExposeProxy();
if (haveAdvice || !isFrozen) {
// If exposing the proxy, then AOP_PROXY must be used.
if (exposeProxy) {
@@ -921,63 +927,18 @@ class CglibAopProxy implements AopProxy, Serializable {
return false;
}
AdvisedSupport otherAdvised = otherCallbackFilter.advised;
if (this.advised.isFrozen() != otherAdvised.isFrozen()) {
return false;
}
if (this.advised.isExposeProxy() != otherAdvised.isExposeProxy()) {
return false;
}
if (this.advised.getTargetSource().isStatic() != otherAdvised.getTargetSource().isStatic()) {
return false;
}
if (!AopProxyUtils.equalsProxiedInterfaces(this.advised, otherAdvised)) {
return false;
}
// Advice instance identity is unimportant to the proxy class:
// All that matters is type and ordering.
if (this.advised.getAdvisorCount() != otherAdvised.getAdvisorCount()) {
return false;
}
Advisor[] thisAdvisors = this.advised.getAdvisors();
Advisor[] thatAdvisors = otherAdvised.getAdvisors();
for (int i = 0; i < thisAdvisors.length; i++) {
Advisor thisAdvisor = thisAdvisors[i];
Advisor thatAdvisor = thatAdvisors[i];
if (!equalsAdviceClasses(thisAdvisor, thatAdvisor)) {
return false;
}
if (!equalsPointcuts(thisAdvisor, thatAdvisor)) {
return false;
}
}
return true;
}
private static boolean equalsAdviceClasses(Advisor a, Advisor b) {
return (a.getAdvice().getClass() == b.getAdvice().getClass());
}
private static boolean equalsPointcuts(Advisor a, Advisor b) {
// If only one of the advisor (but not both) is PointcutAdvisor, then it is a mismatch.
// Takes care of the situations where an IntroductionAdvisor is used (see SPR-3959).
return (!(a instanceof PointcutAdvisor pointcutAdvisor1) ||
(b instanceof PointcutAdvisor pointcutAdvisor2 &&
ObjectUtils.nullSafeEquals(pointcutAdvisor1.getPointcut(), pointcutAdvisor2.getPointcut())));
return (this.advised.getAdvisorKey().equals(otherAdvised.getAdvisorKey()) &&
AopProxyUtils.equalsProxiedInterfaces(this.advised, otherAdvised) &&
ObjectUtils.nullSafeEquals(this.advised.getTargetClass(), otherAdvised.getTargetClass()) &&
this.advised.getTargetSource().isStatic() == otherAdvised.getTargetSource().isStatic() &&
this.advised.isFrozen() == otherAdvised.isFrozen() &&
this.advised.isExposeProxy() == otherAdvised.isExposeProxy() &&
this.advised.isOpaque() == otherAdvised.isOpaque());
}
@Override
public int hashCode() {
int hashCode = 0;
Advisor[] advisors = this.advised.getAdvisors();
for (Advisor advisor : advisors) {
Advice advice = advisor.getAdvice();
hashCode = 13 * hashCode + advice.getClass().hashCode();
}
hashCode = 13 * hashCode + (this.advised.isFrozen() ? 1 : 0);
hashCode = 13 * hashCode + (this.advised.isExposeProxy() ? 1 : 0);
hashCode = 13 * hashCode + (this.advised.isOptimize() ? 1 : 0);
hashCode = 13 * hashCode + (this.advised.isOpaque() ? 1 : 0);
return hashCode;
return this.advised.getAdvisorKey().hashCode();
}
}
@@ -47,6 +47,13 @@ import org.springframework.lang.Nullable;
@SuppressWarnings("serial")
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
/**
* Singleton instance of this class.
* @since 6.0.10
*/
public static final DefaultAdvisorChainFactory INSTANCE = new DefaultAdvisorChainFactory();
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
@@ -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.
@@ -48,6 +48,12 @@ import org.springframework.util.ClassUtils;
*/
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
/**
* Singleton instance of this class.
* @since 6.0.10
*/
public static final DefaultAopProxyFactory INSTANCE = new DefaultAopProxyFactory();
private static final long serialVersionUID = 7930414337282325166L;
@@ -120,18 +120,39 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
if (classLoader == null || classLoader.getParent() == null) {
// JDK bootstrap loader or platform loader suggested ->
// use higher-level loader which can see Spring infrastructure classes
classLoader = getClass().getClassLoader();
}
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
return Proxy.newProxyInstance(determineClassLoader(classLoader), this.proxiedInterfaces, this);
}
@SuppressWarnings("deprecation")
@Override
public Class<?> getProxyClass(@Nullable ClassLoader classLoader) {
return Proxy.getProxyClass(classLoader, this.proxiedInterfaces);
return Proxy.getProxyClass(determineClassLoader(classLoader), this.proxiedInterfaces);
}
/**
* Determine whether the JDK bootstrap or platform loader has been suggested ->
* use higher-level loader which can see Spring infrastructure classes instead.
*/
private ClassLoader determineClassLoader(@Nullable ClassLoader classLoader) {
if (classLoader == null) {
// JDK bootstrap loader -> use spring-aop ClassLoader instead.
return getClass().getClassLoader();
}
if (classLoader.getParent() == null) {
// Potentially the JDK platform loader on JDK 9+
ClassLoader aopClassLoader = getClass().getClassLoader();
ClassLoader aopParent = aopClassLoader.getParent();
while (aopParent != null) {
if (classLoader == aopParent) {
// Suggested ClassLoader is ancestor of spring-aop ClassLoader
// -> use spring-aop ClassLoader itself instead.
return aopClassLoader;
}
aopParent = aopParent.getParent();
}
}
// Regular case: use suggested ClassLoader as-is.
return classLoader;
}
/**
@@ -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.
@@ -44,7 +44,7 @@ public class ProxyCreatorSupport extends AdvisedSupport {
* Create a new ProxyCreatorSupport instance.
*/
public ProxyCreatorSupport() {
this.aopProxyFactory = new DefaultAopProxyFactory();
this.aopProxyFactory = DefaultAopProxyFactory.INSTANCE;
}
/**
@@ -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.
@@ -606,11 +606,6 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
throw new UnsupportedOperationException("Cannot invoke methods: " + this.message);
}
@Override
public boolean isPerInstance() {
throw new UnsupportedOperationException("Cannot invoke methods: " + this.message);
}
@Override
public String toString() {
return this.message;
@@ -34,6 +34,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Convenient superclass for
@@ -82,6 +83,11 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
return this.beanFactory;
}
private ConfigurableBeanFactory getConfigurableBeanFactory() {
Assert.state(this.beanFactory != null, "BeanFactory not set");
return this.beanFactory;
}
//---------------------------------------------------------------------
// Implementation of the TargetSourceCreator interface
@@ -105,7 +111,7 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
// We need to override just this bean definition, as it may reference other beans
// and we're happy to take the parent's definition for those.
// Always use prototype scope if demanded.
BeanDefinition bd = this.beanFactory.getMergedBeanDefinition(beanName);
BeanDefinition bd = getConfigurableBeanFactory().getMergedBeanDefinition(beanName);
GenericBeanDefinition bdCopy = new GenericBeanDefinition(bd);
if (isPrototypeBased()) {
bdCopy.setScope(BeanDefinition.SCOPE_PROTOTYPE);
@@ -127,7 +133,7 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
protected DefaultListableBeanFactory getInternalBeanFactoryForBean(String beanName) {
synchronized (this.internalBeanFactories) {
return this.internalBeanFactories.computeIfAbsent(beanName,
name -> buildInternalBeanFactory(this.beanFactory));
name -> buildInternalBeanFactory(getConfigurableBeanFactory()));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,22 +58,12 @@ public abstract class AbstractPointcutAdvisor implements PointcutAdvisor, Ordere
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof PointcutAdvisor otherAdvisor)) {
return false;
}
return (ObjectUtils.nullSafeEquals(getAdvice(), otherAdvisor.getAdvice()) &&
ObjectUtils.nullSafeEquals(getPointcut(), otherAdvisor.getPointcut()));
return (this == other || (other instanceof PointcutAdvisor otherAdvisor &&
ObjectUtils.nullSafeEquals(getAdvice(), otherAdvisor.getAdvice()) &&
ObjectUtils.nullSafeEquals(getPointcut(), otherAdvisor.getPointcut())));
}
@Override
@@ -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.
@@ -196,14 +196,9 @@ public abstract class AbstractRegexpMethodPointcut extends StaticMethodMatcherPo
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AbstractRegexpMethodPointcut otherPointcut)) {
return false;
}
return (Arrays.equals(this.patterns, otherPointcut.patterns) &&
Arrays.equals(this.excludedPatterns, otherPointcut.excludedPatterns));
return (this == other || (other instanceof AbstractRegexpMethodPointcut otherPointcut &&
Arrays.equals(this.patterns, otherPointcut.patterns) &&
Arrays.equals(this.excludedPatterns, otherPointcut.excludedPatterns)));
}
@Override
@@ -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.
@@ -187,14 +187,9 @@ public class ComposablePointcut implements Pointcut, Serializable {
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof ComposablePointcut otherPointcut)) {
return false;
}
return (this.classFilter.equals(otherPointcut.classFilter) &&
this.methodMatcher.equals(otherPointcut.methodMatcher));
return (this == other || (other instanceof ComposablePointcut otherPointcut &&
this.classFilter.equals(otherPointcut.classFilter) &&
this.methodMatcher.equals(otherPointcut.methodMatcher)));
}
@Override
@@ -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.
@@ -125,13 +125,9 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof ControlFlowPointcut that)) {
return false;
}
return (this.clazz.equals(that.clazz)) && ObjectUtils.nullSafeEquals(this.methodName, that.methodName);
return (this == other || (other instanceof ControlFlowPointcut that &&
this.clazz.equals(that.clazz)) &&
ObjectUtils.nullSafeEquals(this.methodName, that.methodName));
}
@Override
@@ -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.
@@ -134,11 +134,6 @@ public class DefaultIntroductionAdvisor implements IntroductionAdvisor, ClassFil
return this.advice;
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public ClassFilter getClassFilter() {
return this;
@@ -152,13 +147,9 @@ public class DefaultIntroductionAdvisor implements IntroductionAdvisor, ClassFil
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof DefaultIntroductionAdvisor otherAdvisor)) {
return false;
}
return (this.advice.equals(otherAdvisor.advice) && this.interfaces.equals(otherAdvisor.interfaces));
return (this == other || (other instanceof DefaultIntroductionAdvisor otherAdvisor &&
this.advice.equals(otherAdvisor.advice) &&
this.interfaces.equals(otherAdvisor.interfaces)));
}
@Override
@@ -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.
@@ -143,13 +143,8 @@ public abstract class MethodMatchers {
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof UnionMethodMatcher that)) {
return false;
}
return (this.mm1.equals(that.mm1) && this.mm2.equals(that.mm2));
return (this == other || (other instanceof UnionMethodMatcher that &&
this.mm1.equals(that.mm1) && this.mm2.equals(that.mm2)));
}
@Override
@@ -307,13 +302,8 @@ public abstract class MethodMatchers {
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof IntersectionMethodMatcher that)) {
return false;
}
return (this.mm1.equals(that.mm1) && this.mm2.equals(that.mm2));
return (this == other || (other instanceof IntersectionMethodMatcher that &&
this.mm1.equals(that.mm1) && this.mm2.equals(that.mm2)));
}
@Override
@@ -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.
@@ -77,11 +77,6 @@ public abstract class StaticMethodMatcherPointcutAdvisor extends StaticMethodMat
return this.advice;
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public Pointcut getPointcut() {
return this;
@@ -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.
@@ -68,13 +68,9 @@ public class AnnotationClassFilter implements ClassFilter {
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AnnotationClassFilter otherCf)) {
return false;
}
return (this.annotationType.equals(otherCf.annotationType) && this.checkInherited == otherCf.checkInherited);
return (this == other || (other instanceof AnnotationClassFilter otherCf &&
this.annotationType.equals(otherCf.annotationType) &&
this.checkInherited == otherCf.checkInherited));
}
@Override
@@ -121,14 +121,9 @@ public class AnnotationMatchingPointcut implements Pointcut {
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AnnotationMatchingPointcut otherPointcut)) {
return false;
}
return (this.classFilter.equals(otherPointcut.classFilter) &&
this.methodMatcher.equals(otherPointcut.methodMatcher));
return (this == other || (other instanceof AnnotationMatchingPointcut otherPointcut &&
this.classFilter.equals(otherPointcut.classFilter) &&
this.methodMatcher.equals(otherPointcut.methodMatcher)));
}
@Override
@@ -183,14 +178,9 @@ public class AnnotationMatchingPointcut implements Pointcut {
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof AnnotationCandidateClassFilter that)) {
return false;
}
return this.annotationType.equals(that.annotationType);
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof AnnotationCandidateClassFilter that &&
this.annotationType.equals(that.annotationType)));
}
@Override
@@ -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.
@@ -27,10 +27,9 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Simple {@link org.springframework.aop.MethodMatcher MethodMatcher} that looks
* for a specific annotation being present on a method (checking both the method
* on the invoked interface, if any, and the corresponding method on the target
* class).
* Simple {@link org.springframework.aop.MethodMatcher MethodMatcher} that looks for
* a specific annotation being present on a method (checking both the method on the
* invoked interface, if any, and the corresponding method on the target class).
*
* @author Juergen Hoeller
* @author Sam Brannen
@@ -90,13 +89,9 @@ public class AnnotationMethodMatcher extends StaticMethodMatcher {
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AnnotationMethodMatcher otherMm)) {
return false;
}
return (this.annotationType.equals(otherMm.annotationType) && this.checkInherited == otherMm.checkInherited);
return (this == other || (other instanceof AnnotationMethodMatcher otherMm &&
this.annotationType.equals(otherMm.annotationType) &&
this.checkInherited == otherMm.checkInherited));
}
@Override
@@ -25,6 +25,7 @@ import org.springframework.aop.TargetSource;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
@@ -58,16 +59,18 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
protected final transient Log logger = LogFactory.getLog(getClass());
/** Name of the target bean we will create on each invocation. */
@Nullable
private String targetBeanName;
/** Class of the target. */
@Nullable
private volatile Class<?> targetClass;
/**
* BeanFactory that owns this TargetSource. We need to hold onto this
* reference so that we can create new prototype instances as necessary.
*/
@SuppressWarnings("serial")
@Nullable
private BeanFactory beanFactory;
@@ -88,6 +91,7 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
* Return the name of the target bean in the factory.
*/
public String getTargetBeanName() {
Assert.state(this.targetBeanName != null, "Target bean name not set");
return this.targetBeanName;
}
@@ -117,11 +121,13 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
* Return the owning BeanFactory.
*/
public BeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null, "BeanFactory not set");
return this.beanFactory;
}
@Override
@Nullable
public Class<?> getTargetClass() {
Class<?> targetClass = this.targetClass;
if (targetClass != null) {
@@ -130,7 +136,7 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
synchronized (this) {
// Full check within synchronization, entering the BeanFactory interaction algorithm only once...
targetClass = this.targetClass;
if (targetClass == null && this.beanFactory != null) {
if (targetClass == null && this.beanFactory != null && this.targetBeanName != null) {
// Determine type of the target bean.
targetClass = this.beanFactory.getType(this.targetBeanName);
if (targetClass == null) {
@@ -184,18 +190,16 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
@Override
public int hashCode() {
int hashCode = getClass().hashCode();
hashCode = 13 * hashCode + ObjectUtils.nullSafeHashCode(this.beanFactory);
hashCode = 13 * hashCode + ObjectUtils.nullSafeHashCode(this.targetBeanName);
return hashCode;
return getClass().hashCode() * 13 + ObjectUtils.nullSafeHashCode(this.targetBeanName);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(getClass().getSimpleName());
sb.append(" for target bean '").append(this.targetBeanName).append('\'');
if (this.targetClass != null) {
sb.append(" of type [").append(this.targetClass.getName()).append(']');
Class<?> targetClass = this.targetClass;
if (targetClass != null) {
sb.append(" of type [").append(targetClass.getName()).append(']');
}
return sb.toString();
}
@@ -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.
@@ -46,6 +46,7 @@ public abstract class AbstractLazyCreationTargetSource implements TargetSource {
protected final Log logger = LogFactory.getLog(getClass());
/** The lazily initialized target object. */
@Nullable
private Object lazyTarget;
@@ -70,6 +70,7 @@ public final class EmptyTargetSource implements TargetSource, Serializable {
// Instance implementation
//---------------------------------------------------------------------
@Nullable
private final Class<?> targetClass;
private final boolean isStatic;
@@ -97,12 +97,11 @@ public class HotSwappableTargetSource implements TargetSource, Serializable {
/**
* Two HotSwappableTargetSources are equal if the current target
* objects are equal.
* Two HotSwappableTargetSources are equal if the current target objects are equal.
*/
@Override
public boolean equals(@Nullable Object obj) {
return (this == obj || (obj instanceof HotSwappableTargetSource that &&
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof HotSwappableTargetSource that &&
this.target.equals(that.target)));
}
@@ -84,13 +84,8 @@ public class SingletonTargetSource implements TargetSource, Serializable {
*/
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof SingletonTargetSource otherTargetSource)) {
return false;
}
return this.target.equals(otherTargetSource.target);
return (this == other || (other instanceof SingletonTargetSource that &&
this.target.equals(that.target)));
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,7 +58,12 @@ public class ThreadLocalTargetSource extends AbstractPrototypeBasedTargetSource
* is meant to be per thread per instance of the ThreadLocalTargetSource class.
*/
private final ThreadLocal<Object> targetInThread =
new NamedThreadLocal<>("Thread-local instance of bean '" + getTargetBeanName() + "'");
new NamedThreadLocal<>("Thread-local instance of bean") {
@Override
public String toString() {
return super.toString() + " '" + getTargetBeanName() + "'";
}
};
/**
* Set of managed targets, enabling us to keep track of the targets we've created.
@@ -389,7 +389,9 @@ public class ProxyFactoryTests {
CharSequence target = "test";
ProxyFactory pf = new ProxyFactory(target);
ClassLoader cl = target.getClass().getClassLoader();
assertThat(((CharSequence) pf.getProxy(cl)).toString()).isEqualTo(target);
CharSequence proxy = (CharSequence) pf.getProxy(cl);
assertThat(proxy.toString()).isEqualTo(target);
assertThat(pf.getProxyClass(cl)).isSameAs(proxy.getClass());
}
@Test
@@ -398,7 +400,9 @@ public class ProxyFactoryTests {
ProxyFactory pf = new ProxyFactory(target);
pf.setProxyTargetClass(true);
ClassLoader cl = target.getClass().getClassLoader();
assertThat(((Date) pf.getProxy(cl)).getTime()).isEqualTo(target.getTime());
Date proxy = (Date) pf.getProxy(cl);
assertThat(proxy.getTime()).isEqualTo(target.getTime());
assertThat(pf.getProxyClass(cl)).isSameAs(proxy.getClass());
}
@Test
@@ -415,7 +419,9 @@ public class ProxyFactoryTests {
};
ProxyFactory pf = new ProxyFactory(target);
ClassLoader cl = Savepoint.class.getClassLoader();
assertThat(((Savepoint) pf.getProxy(cl)).getSavepointName()).isEqualTo("sp");
Savepoint proxy = (Savepoint) pf.getProxy(cl);
assertThat(proxy.getSavepointName()).isEqualTo("sp");
assertThat(pf.getProxyClass(cl)).isSameAs(proxy.getClass());
}
@@ -0,0 +1,43 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.core.io.ResourceEditor;
import org.springframework.lang.Nullable;
/**
* {@link RuntimeHintsRegistrar} to register hints for popular conventions in
* {@link BeanUtils#findEditorByConvention(Class)}.
*
* @author Sebastien Deleuze
* @since 6.0.10
*/
class BeanUtilsRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
ReflectionHints reflectionHints = hints.reflection();
reflectionHints.registerType(ResourceEditor.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
reflectionHints.registerTypeIfPresent(classLoader, "org.springframework.http.MediaTypeEditor",
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
}
}
@@ -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.
@@ -63,8 +63,8 @@ public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport
(field != null ? new TypeDescriptor(field) : TypeDescriptor.valueOf(requiredType)));
}
@Nullable
@Override
@Nullable
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
@@ -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.
@@ -136,7 +136,10 @@ public interface BeanFactory {
* <p>Translates aliases back to the corresponding canonical bean name.
* <p>Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to retrieve
* @return an instance of the bean
* @return an instance of the bean.
* Note that the return value will never be {@code null} but possibly a stub for
* {@code null} returned from a factory method, to be checked via {@code equals(null)}.
* Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies.
* @throws NoSuchBeanDefinitionException if there is no bean with the specified name
* @throws BeansException if the bean could not be obtained
*/
@@ -152,7 +155,11 @@ public interface BeanFactory {
* <p>Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to retrieve
* @param requiredType type the bean must match; can be an interface or superclass
* @return an instance of the bean
* @return an instance of the bean.
* Note that the return value will never be {@code null}. In case of a stub for
* {@code null} from a factory method having been resolved for the requested bean, a
* {@code BeanNotOfRequiredTypeException} against the NullBean stub will be raised.
* Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies.
* @throws NoSuchBeanDefinitionException if there is no such bean definition
* @throws BeanNotOfRequiredTypeException if the bean is not of the required type
* @throws BeansException if the bean could not be created
@@ -286,7 +286,8 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
String beanName = registeredBean.getBeanName();
RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition();
InjectionMetadata metadata = findInjectionMetadata(beanName, beanClass, beanDefinition);
Collection<AutowiredElement> autowiredElements = getAutowiredElements(metadata);
Collection<AutowiredElement> autowiredElements = getAutowiredElements(metadata,
registeredBean.getMergedBeanDefinition().getPropertyValues());
if (!ObjectUtils.isEmpty(autowiredElements)) {
return new AotContribution(beanClass, autowiredElements, getAutowireCandidateResolver());
}
@@ -295,8 +296,8 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
@SuppressWarnings({ "rawtypes", "unchecked" })
private Collection<AutowiredElement> getAutowiredElements(InjectionMetadata metadata) {
return (Collection) metadata.getInjectedElements();
private Collection<AutowiredElement> getAutowiredElements(InjectionMetadata metadata, PropertyValues propertyValues) {
return (Collection) metadata.getInjectedElements(propertyValues);
}
@Nullable
@@ -718,7 +719,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
if (value != null || this.required) {
cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
if (value != null && autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
@@ -752,7 +753,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
if (checkPropertySkipping(pvs)) {
if (!shouldInject(pvs)) {
return;
}
Method method = (Method) this.member;
@@ -828,7 +829,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
String autowiredBeanName = it.next();
if (beanFactory.containsBean(autowiredBeanName) &&
if (arguments[i] != null && beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
descriptors[i], autowiredBeanName, paramTypes[i]);
@@ -97,6 +97,18 @@ public class InjectionMetadata {
return Collections.unmodifiableCollection(this.injectedElements);
}
/**
* Return the {@link InjectedElement elements} to inject based on the
* specified {@link PropertyValues}. If a property is already defined
* for an {@link InjectedElement}, it is excluded.
* @param pvs the property values to consider
* @return the elements to inject
* @since 6.0.10
*/
public Collection<InjectedElement> getInjectedElements(@Nullable PropertyValues pvs) {
return this.injectedElements.stream().filter(candidate -> candidate.shouldInject(pvs)).toList();
}
/**
* Determine whether this metadata instance needs to be refreshed.
* @param clazz the current target class
@@ -104,7 +116,7 @@ public class InjectionMetadata {
* @since 5.2.4
*/
protected boolean needsRefresh(Class<?> clazz) {
return this.targetClass != clazz;
return (this.targetClass != clazz);
}
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
@@ -230,21 +242,34 @@ public class InjectionMetadata {
}
}
/**
* Whether the property values should be injected.
* @param pvs property values to check
* @return whether the property values should be injected
* @since 6.0.10
*/
protected boolean shouldInject(@Nullable PropertyValues pvs) {
if (this.isField) {
return true;
}
return !checkPropertySkipping(pvs);
}
/**
* Either this or {@link #getResourceToInject} needs to be overridden.
*/
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
throws Throwable {
if (!shouldInject(pvs)) {
return;
}
if (this.isField) {
Field field = (Field) this.member;
ReflectionUtils.makeAccessible(field);
field.set(target, getResourceToInject(target, requestingBeanName));
}
else {
if (checkPropertySkipping(pvs)) {
return;
}
try {
Method method = (Method) this.member;
ReflectionUtils.makeAccessible(method);
@@ -102,7 +102,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
private boolean singletonsCurrentlyInDestruction = false;
/** Disposable bean instances: bean name to disposable instance. */
private final Map<String, Object> disposableBeans = new LinkedHashMap<>();
private final Map<String, DisposableBean> disposableBeans = new LinkedHashMap<>();
/** Map between containing bean names: bean name to Set of bean names that the bean contains. */
private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
@@ -554,7 +554,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
// Destroy the corresponding DisposableBean instance.
DisposableBean disposableBean;
synchronized (this.disposableBeans) {
disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
disposableBean = this.disposableBeans.remove(beanName);
}
destroyBean(beanName, disposableBean);
}
@@ -65,8 +65,8 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
private static final String SHUTDOWN_METHOD_NAME = "shutdown";
private static final Log logger = LogFactory.getLog(DisposableBeanAdapter.class);
private static final Log logger = LogFactory.getLog(DisposableBeanAdapter.class);
private final Object bean;
@@ -240,7 +240,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
}
}
else if (this.destroyMethodNames != null) {
for (String destroyMethodName: this.destroyMethodNames) {
for (String destroyMethodName : this.destroyMethodNames) {
Method destroyMethod = determineDestroyMethod(destroyMethodName);
if (destroyMethod != null) {
invokeCustomDestroyMethod(
@@ -288,7 +288,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
*/
private void invokeCustomDestroyMethod(Method destroyMethod) {
int paramCount = destroyMethod.getParameterCount();
final Object[] args = new Object[paramCount];
Object[] args = new Object[paramCount];
if (paramCount == 1) {
args[0] = Boolean.TRUE;
}
@@ -212,6 +212,15 @@ public final class RegisteredBean {
.resolveConstructorOrFactoryMethod(getBeanName(), getMergedBeanDefinition());
}
/**
* Resolve an autowired argument.
* @param descriptor the descriptor for the dependency (field/method/constructor)
* @param typeConverter the TypeConverter to use for populating arrays and collections
* @param autowiredBeans a Set that all names of autowired beans (used for
* resolving the given dependency) are supposed to be added to
* @return the resolved object, or {@code null} if none found
* @since 6.0.9
*/
@Nullable
public Object resolveAutowiredArgument(DependencyDescriptor descriptor, TypeConverter typeConverter,
Set<String> autowiredBeans) {
@@ -1,5 +1,6 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=\
org.springframework.beans.factory.annotation.JakartaAnnotationsRuntimeHints
org.springframework.beans.factory.annotation.JakartaAnnotationsRuntimeHints,\
org.springframework.beans.BeanUtilsRuntimeHints
org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\
org.springframework.beans.factory.aot.BeanRegistrationsAotProcessor
@@ -0,0 +1,56 @@
/*
* 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 org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.core.io.ResourceEditor;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.util.ClassUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link BeanUtilsRuntimeHints}.
*
* @author Sebastien Deleuze
* @since 6.0.10
* @see org.springframework.http.WebBeanUtilsRuntimeHintsTests
*/
class BeanUtilsRuntimeHintsTests {
private final RuntimeHints hints = new RuntimeHints();
@BeforeEach
void setup() {
SpringFactoriesLoader.forResourceLocation("META-INF/spring/aot.factories")
.load(RuntimeHintsRegistrar.class)
.forEach(registrar -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));
}
@Test
void resourceEditorHasHints() {
assertThat(RuntimeHintsPredicates.reflection().onType(ResourceEditor.class)
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints);
}
}
@@ -130,6 +130,27 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getTestBean2()).isSameAs(tb);
}
@Test
@SuppressWarnings("rawtypes")
void resourceInjectionWithNullBean() {
RootBeanDefinition bd = new RootBeanDefinition(NonPublicResourceInjectionBean.class);
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd);
RootBeanDefinition tb = new RootBeanDefinition(NullFactoryMethods.class);
tb.setFactoryMethodName("createTestBean");
bf.registerBeanDefinition("testBean", tb);
NonPublicResourceInjectionBean bean = (NonPublicResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
bean = (NonPublicResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
}
@Test
void extendedResourceInjection() {
RootBeanDefinition bd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class);
@@ -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.
@@ -65,11 +65,15 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
private final DefaultListableBeanFactory beanFactory;
private final AutowiredAnnotationBeanPostProcessor beanPostProcessor;
AutowiredAnnotationBeanRegistrationAotContributionTests() {
this.generationContext = new TestGenerationContext();
this.beanRegistrationCode = new MockBeanRegistrationCode(this.generationContext);
this.beanFactory = new DefaultListableBeanFactory();
this.beanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
this.beanPostProcessor.setBeanFactory(this.beanFactory);
}
@@ -185,10 +189,19 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
});
}
@Test
void contributeWhenMethodInjectionHasMatchingPropertyValue() {
RootBeanDefinition beanDefinition = new RootBeanDefinition(InjectionBean.class);
beanDefinition.getPropertyValues().addPropertyValue("counter", 42);
this.beanFactory.registerBeanDefinition("test", beanDefinition);
BeanRegistrationAotContribution contribution = this.beanPostProcessor
.processAheadOfTime(RegisteredBean.of(this.beanFactory, "test"));
assertThat(contribution).isNull();
}
private RegisteredBean getAndApplyContribution(Class<?> beanClass) {
RegisteredBean registeredBean = registerBean(beanClass);
BeanRegistrationAotContribution contribution = new AutowiredAnnotationBeanPostProcessor()
.processAheadOfTime(registeredBean);
BeanRegistrationAotContribution contribution = this.beanPostProcessor.processAheadOfTime(registeredBean);
assertThat(contribution).isNotNull();
contribution.applyTo(this.generationContext, this.beanRegistrationCode);
return registeredBean;
@@ -229,4 +242,16 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
result.accept(compiled.getInstance(BiFunction.class), compiled));
}
static class InjectionBean {
@SuppressWarnings("unused")
private Integer counter;
@Autowired
public void setCounter(Integer counter) {
this.counter = counter;
}
}
}
@@ -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.
@@ -16,30 +16,30 @@
package org.springframework.cache.jcache.interceptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* Advisor driven by a {@link JCacheOperationSource}, used to include a
* cache advice bean for methods that are cacheable.
*
* @author Stephane Nicoll
* @author Juergen Hoeller
* @since 4.1
* @see #setAdviceBeanName
* @see JCacheInterceptor
*/
@SuppressWarnings("serial")
public class BeanFactoryJCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
@Nullable
private JCacheOperationSource cacheOperationSource;
private final JCacheOperationSourcePointcut pointcut = new JCacheOperationSourcePointcut() {
@Override
protected JCacheOperationSource getCacheOperationSource() {
return cacheOperationSource;
}
};
private final JCacheOperationSourcePointcut pointcut = new JCacheOperationSourcePointcut();
/**
@@ -48,7 +48,7 @@ public class BeanFactoryJCacheOperationSourceAdvisor extends AbstractBeanFactory
* set on the cache interceptor itself.
*/
public void setCacheOperationSource(JCacheOperationSource cacheOperationSource) {
this.cacheOperationSource = cacheOperationSource;
this.pointcut.setCacheOperationSource(cacheOperationSource);
}
/**
@@ -64,4 +64,37 @@ public class BeanFactoryJCacheOperationSourceAdvisor extends AbstractBeanFactory
return this.pointcut;
}
private static class JCacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
@Nullable
private JCacheOperationSource cacheOperationSource;
public void setCacheOperationSource(@Nullable JCacheOperationSource cacheOperationSource) {
this.cacheOperationSource = cacheOperationSource;
}
@Override
public boolean matches(Method method, Class<?> targetClass) {
return (this.cacheOperationSource == null ||
this.cacheOperationSource.getCacheOperation(method, targetClass) != null);
}
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof JCacheOperationSourcePointcut otherPc &&
ObjectUtils.nullSafeEquals(this.cacheOperationSource, otherPc.cacheOperationSource)));
}
@Override
public int hashCode() {
return JCacheOperationSourcePointcut.class.hashCode();
}
@Override
public String toString() {
return getClass().getName() + ": " + this.cacheOperationSource;
}
}
}
@@ -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.
@@ -29,7 +29,9 @@ import org.springframework.util.ObjectUtils;
*
* @author Stephane Nicoll
* @since 4.1
* @deprecated since 6.0.10, as it is not used by the framework anymore
*/
@Deprecated(since = "6.0.10", forRemoval = true)
@SuppressWarnings("serial")
public abstract class JCacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
@@ -200,7 +200,7 @@ public class JavaMailSenderImpl implements JavaMailSender {
* Set the mail server port.
* <p>Default is {@link #DEFAULT_PORT}, letting JavaMail use the default
* SMTP port (25).
*/
*/
public void setPort(int port) {
this.port = port;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,17 +16,18 @@
package org.springframework.mail.javamail;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Properties;
import java.util.stream.Stream;
import jakarta.activation.FileTypeMap;
import jakarta.mail.Address;
import jakarta.mail.Message;
import jakarta.mail.Message.RecipientType;
import jakarta.mail.MessagingException;
import jakarta.mail.NoSuchProviderException;
import jakarta.mail.Session;
@@ -44,18 +45,22 @@ import org.springframework.util.ObjectUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.Assertions.entry;
/**
* @author Juergen Hoeller
* @author Stephane Nicoll
* @author Sam Brannen
* @since 09.10.2004
*/
public class JavaMailSenderTests {
class JavaMailSenderTests {
private final MockJavaMailSender sender = new MockJavaMailSender();
@Test
public void javaMailSenderWithSimpleMessage() throws MessagingException, IOException {
MockJavaMailSender sender = new MockJavaMailSender();
void javaMailSenderWithSimpleMessage() throws Exception {
sender.setHost("host");
sender.setPort(30);
sender.setUsername("username");
@@ -81,30 +86,19 @@ public class JavaMailSenderTests {
assertThat(sender.transport.getSentMessages()).hasSize(1);
MimeMessage sentMessage = sender.transport.getSentMessage(0);
List<Address> froms = Arrays.asList(sentMessage.getFrom());
assertThat(froms).hasSize(1);
assertThat(((InternetAddress) froms.get(0)).getAddress()).isEqualTo("me@mail.org");
List<Address> replyTos = Arrays.asList(sentMessage.getReplyTo());
assertThat(((InternetAddress) replyTos.get(0)).getAddress()).isEqualTo("reply@mail.org");
List<Address> tos = Arrays.asList(sentMessage.getRecipients(Message.RecipientType.TO));
assertThat(tos).hasSize(1);
assertThat(((InternetAddress) tos.get(0)).getAddress()).isEqualTo("you@mail.org");
List<Address> ccs = Arrays.asList(sentMessage.getRecipients(Message.RecipientType.CC));
assertThat(ccs).hasSize(2);
assertThat(((InternetAddress) ccs.get(0)).getAddress()).isEqualTo("he@mail.org");
assertThat(((InternetAddress) ccs.get(1)).getAddress()).isEqualTo("she@mail.org");
List<Address> bccs = Arrays.asList(sentMessage.getRecipients(Message.RecipientType.BCC));
assertThat(bccs).hasSize(2);
assertThat(((InternetAddress) bccs.get(0)).getAddress()).isEqualTo("us@mail.org");
assertThat(((InternetAddress) bccs.get(1)).getAddress()).isEqualTo("them@mail.org");
assertThat(addresses(sentMessage.getFrom())).containsExactly("me@mail.org");
assertThat(addresses(sentMessage.getReplyTo())).containsExactly("reply@mail.org");
assertThat(addresses(sentMessage.getRecipients(RecipientType.TO))).containsExactly("you@mail.org");
assertThat(addresses(sentMessage.getRecipients(RecipientType.CC))).containsExactly("he@mail.org", "she@mail.org");
assertThat(addresses(sentMessage.getRecipients(RecipientType.BCC))).containsExactly("us@mail.org", "them@mail.org");
assertThat(sentMessage.getSentDate().getTime()).isEqualTo(sentDate.getTime());
assertThat(sentMessage.getSubject()).isEqualTo("my subject");
assertThat(sentMessage.getContent()).isEqualTo("my text");
}
@Test
public void javaMailSenderWithSimpleMessages() throws MessagingException {
MockJavaMailSender sender = new MockJavaMailSender();
void javaMailSenderWithSimpleMessages() throws Exception {
sender.setHost("host");
sender.setUsername("username");
sender.setPassword("password");
@@ -122,59 +116,49 @@ public class JavaMailSenderTests {
assertThat(sender.transport.getSentMessages()).hasSize(2);
MimeMessage sentMessage1 = sender.transport.getSentMessage(0);
List<Address> tos1 = Arrays.asList(sentMessage1.getRecipients(Message.RecipientType.TO));
assertThat(tos1).hasSize(1);
assertThat(((InternetAddress) tos1.get(0)).getAddress()).isEqualTo("he@mail.org");
MimeMessage sentMessage2 = sender.transport.getSentMessage(1);
List<Address> tos2 = Arrays.asList(sentMessage2.getRecipients(Message.RecipientType.TO));
assertThat(tos2).hasSize(1);
assertThat(((InternetAddress) tos2.get(0)).getAddress()).isEqualTo("she@mail.org");
assertThat(addresses(sentMessage1.getRecipients(RecipientType.TO))).containsExactly("he@mail.org");
assertThat(addresses(sentMessage2.getRecipients(RecipientType.TO))).containsExactly("she@mail.org");
}
@Test
public void javaMailSenderWithMimeMessage() throws MessagingException {
MockJavaMailSender sender = new MockJavaMailSender();
void javaMailSenderWithMimeMessage() throws Exception {
sender.setHost("host");
sender.setUsername("username");
sender.setPassword("password");
MimeMessage mimeMessage = sender.createMimeMessage();
mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("you@mail.org"));
mimeMessage.setRecipient(RecipientType.TO, new InternetAddress("you@mail.org"));
sender.send(mimeMessage);
assertThat(sender.transport.getConnectedHost()).isEqualTo("host");
assertThat(sender.transport.getConnectedUsername()).isEqualTo("username");
assertThat(sender.transport.getConnectedPassword()).isEqualTo("password");
assertThat(sender.transport.isCloseCalled()).isTrue();
assertThat(sender.transport.getSentMessages()).hasSize(1);
assertThat(sender.transport.getSentMessage(0)).isEqualTo(mimeMessage);
assertThat(sender.transport.getSentMessages()).containsExactly(mimeMessage);
}
@Test
public void javaMailSenderWithMimeMessages() throws MessagingException {
MockJavaMailSender sender = new MockJavaMailSender();
void javaMailSenderWithMimeMessages() throws Exception {
sender.setHost("host");
sender.setUsername("username");
sender.setPassword("password");
MimeMessage mimeMessage1 = sender.createMimeMessage();
mimeMessage1.setRecipient(Message.RecipientType.TO, new InternetAddress("he@mail.org"));
mimeMessage1.setRecipient(RecipientType.TO, new InternetAddress("he@mail.org"));
MimeMessage mimeMessage2 = sender.createMimeMessage();
mimeMessage2.setRecipient(Message.RecipientType.TO, new InternetAddress("she@mail.org"));
mimeMessage2.setRecipient(RecipientType.TO, new InternetAddress("she@mail.org"));
sender.send(mimeMessage1, mimeMessage2);
assertThat(sender.transport.getConnectedHost()).isEqualTo("host");
assertThat(sender.transport.getConnectedUsername()).isEqualTo("username");
assertThat(sender.transport.getConnectedPassword()).isEqualTo("password");
assertThat(sender.transport.isCloseCalled()).isTrue();
assertThat(sender.transport.getSentMessages()).hasSize(2);
assertThat(sender.transport.getSentMessage(0)).isEqualTo(mimeMessage1);
assertThat(sender.transport.getSentMessage(1)).isEqualTo(mimeMessage2);
assertThat(sender.transport.getSentMessages()).containsExactly(mimeMessage1, mimeMessage2);
}
@Test
public void javaMailSenderWithMimeMessagePreparator() {
MockJavaMailSender sender = new MockJavaMailSender();
void javaMailSenderWithMimeMessagePreparator() {
sender.setHost("host");
sender.setUsername("username");
sender.setPassword("password");
@@ -182,7 +166,7 @@ public class JavaMailSenderTests {
final List<Message> messages = new ArrayList<>();
MimeMessagePreparator preparator = mimeMessage -> {
mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("you@mail.org"));
mimeMessage.setRecipient(RecipientType.TO, new InternetAddress("you@mail.org"));
messages.add(mimeMessage);
};
sender.send(preparator);
@@ -191,13 +175,11 @@ public class JavaMailSenderTests {
assertThat(sender.transport.getConnectedUsername()).isEqualTo("username");
assertThat(sender.transport.getConnectedPassword()).isEqualTo("password");
assertThat(sender.transport.isCloseCalled()).isTrue();
assertThat(sender.transport.getSentMessages()).hasSize(1);
assertThat(sender.transport.getSentMessage(0)).isEqualTo(messages.get(0));
assertThat(sender.transport.getSentMessages()).containsExactly(messages.get(0));
}
@Test
public void javaMailSenderWithMimeMessagePreparators() {
MockJavaMailSender sender = new MockJavaMailSender();
void javaMailSenderWithMimeMessagePreparators() {
sender.setHost("host");
sender.setUsername("username");
sender.setPassword("password");
@@ -205,11 +187,11 @@ public class JavaMailSenderTests {
final List<Message> messages = new ArrayList<>();
MimeMessagePreparator preparator1 = mimeMessage -> {
mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("he@mail.org"));
mimeMessage.setRecipient(RecipientType.TO, new InternetAddress("he@mail.org"));
messages.add(mimeMessage);
};
MimeMessagePreparator preparator2 = mimeMessage -> {
mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("she@mail.org"));
mimeMessage.setRecipient(RecipientType.TO, new InternetAddress("she@mail.org"));
messages.add(mimeMessage);
};
sender.send(preparator1, preparator2);
@@ -218,22 +200,19 @@ public class JavaMailSenderTests {
assertThat(sender.transport.getConnectedUsername()).isEqualTo("username");
assertThat(sender.transport.getConnectedPassword()).isEqualTo("password");
assertThat(sender.transport.isCloseCalled()).isTrue();
assertThat(sender.transport.getSentMessages()).hasSize(2);
assertThat(sender.transport.getSentMessage(0)).isEqualTo(messages.get(0));
assertThat(sender.transport.getSentMessage(1)).isEqualTo(messages.get(1));
assertThat(messages).hasSize(2);
assertThat(sender.transport.getSentMessages()).containsExactlyElementsOf(messages);
}
@Test
public void javaMailSenderWithMimeMessageHelper() throws MessagingException {
MockJavaMailSender sender = new MockJavaMailSender();
void javaMailSenderWithMimeMessageHelper() throws Exception {
sender.setHost("host");
sender.setUsername("username");
sender.setPassword("password");
MimeMessageHelper message = new MimeMessageHelper(sender.createMimeMessage());
assertThat(message.getEncoding()).isNull();
boolean condition = message.getFileTypeMap() instanceof ConfigurableMimeFileTypeMap;
assertThat(condition).isTrue();
assertThat(message.getFileTypeMap()).isInstanceOf(ConfigurableMimeFileTypeMap.class);
message.setTo("you@mail.org");
sender.send(message.getMimeMessage());
@@ -242,13 +221,11 @@ public class JavaMailSenderTests {
assertThat(sender.transport.getConnectedUsername()).isEqualTo("username");
assertThat(sender.transport.getConnectedPassword()).isEqualTo("password");
assertThat(sender.transport.isCloseCalled()).isTrue();
assertThat(sender.transport.getSentMessages()).hasSize(1);
assertThat(sender.transport.getSentMessage(0)).isEqualTo(message.getMimeMessage());
assertThat(sender.transport.getSentMessages()).containsExactly(message.getMimeMessage());
}
@Test
public void javaMailSenderWithMimeMessageHelperAndSpecificEncoding() throws MessagingException {
MockJavaMailSender sender = new MockJavaMailSender();
void javaMailSenderWithMimeMessageHelperAndSpecificEncoding() throws Exception {
sender.setHost("host");
sender.setUsername("username");
sender.setPassword("password");
@@ -266,13 +243,11 @@ public class JavaMailSenderTests {
assertThat(sender.transport.getConnectedUsername()).isEqualTo("username");
assertThat(sender.transport.getConnectedPassword()).isEqualTo("password");
assertThat(sender.transport.isCloseCalled()).isTrue();
assertThat(sender.transport.getSentMessages()).hasSize(1);
assertThat(sender.transport.getSentMessage(0)).isEqualTo(message.getMimeMessage());
assertThat(sender.transport.getSentMessages()).containsExactly(message.getMimeMessage());
}
@Test
public void javaMailSenderWithMimeMessageHelperAndDefaultEncoding() throws MessagingException {
MockJavaMailSender sender = new MockJavaMailSender();
void javaMailSenderWithMimeMessageHelperAndDefaultEncoding() throws Exception {
sender.setHost("host");
sender.setUsername("username");
sender.setPassword("password");
@@ -291,41 +266,29 @@ public class JavaMailSenderTests {
assertThat(sender.transport.getConnectedUsername()).isEqualTo("username");
assertThat(sender.transport.getConnectedPassword()).isEqualTo("password");
assertThat(sender.transport.isCloseCalled()).isTrue();
assertThat(sender.transport.getSentMessages()).hasSize(1);
assertThat(sender.transport.getSentMessage(0)).isEqualTo(message.getMimeMessage());
assertThat(sender.transport.getSentMessages()).containsExactly(message.getMimeMessage());
}
@Test
public void javaMailSenderWithParseExceptionOnSimpleMessage() {
MockJavaMailSender sender = new MockJavaMailSender();
void javaMailSenderWithParseExceptionOnSimpleMessage() {
SimpleMailMessage simpleMessage = new SimpleMailMessage();
simpleMessage.setFrom("");
try {
sender.send(simpleMessage);
}
catch (MailParseException ex) {
// expected
boolean condition = ex.getCause() instanceof AddressException;
assertThat(condition).isTrue();
}
assertThatExceptionOfType(MailParseException.class)
.isThrownBy(() -> sender.send(simpleMessage))
.withCauseInstanceOf(AddressException.class);
}
@Test
public void javaMailSenderWithParseExceptionOnMimeMessagePreparator() {
MockJavaMailSender sender = new MockJavaMailSender();
void javaMailSenderWithParseExceptionOnMimeMessagePreparator() {
MimeMessagePreparator preparator = mimeMessage -> mimeMessage.setFrom(new InternetAddress(""));
try {
sender.send(preparator);
}
catch (MailParseException ex) {
// expected
boolean condition = ex.getCause() instanceof AddressException;
assertThat(condition).isTrue();
}
assertThatExceptionOfType(MailParseException.class)
.isThrownBy(() -> sender.send(preparator))
.withCauseInstanceOf(AddressException.class);
}
@Test
public void javaMailSenderWithCustomSession() throws MessagingException {
void javaMailSenderWithCustomSession() throws Exception {
final Session session = Session.getInstance(new Properties());
MockJavaMailSender sender = new MockJavaMailSender() {
@Override
@@ -341,7 +304,7 @@ public class JavaMailSenderTests {
MimeMessage mimeMessage = sender.createMimeMessage();
mimeMessage.setSubject("custom");
mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("you@mail.org"));
mimeMessage.setRecipient(RecipientType.TO, new InternetAddress("you@mail.org"));
mimeMessage.setSentDate(new GregorianCalendar(2005, 3, 1).getTime());
sender.send(mimeMessage);
@@ -349,12 +312,11 @@ public class JavaMailSenderTests {
assertThat(sender.transport.getConnectedUsername()).isEqualTo("username");
assertThat(sender.transport.getConnectedPassword()).isEqualTo("password");
assertThat(sender.transport.isCloseCalled()).isTrue();
assertThat(sender.transport.getSentMessages()).hasSize(1);
assertThat(sender.transport.getSentMessage(0)).isEqualTo(mimeMessage);
assertThat(sender.transport.getSentMessages()).containsExactly(mimeMessage);
}
@Test
public void javaMailProperties() throws MessagingException {
void javaMailProperties() throws Exception {
Properties props = new Properties();
props.setProperty("bogusKey", "bogusValue");
MockJavaMailSender sender = new MockJavaMailSender() {
@@ -370,44 +332,40 @@ public class JavaMailSenderTests {
sender.setPassword("password");
MimeMessage mimeMessage = sender.createMimeMessage();
mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("you@mail.org"));
mimeMessage.setRecipient(RecipientType.TO, new InternetAddress("you@mail.org"));
sender.send(mimeMessage);
assertThat(sender.transport.getConnectedHost()).isEqualTo("host");
assertThat(sender.transport.getConnectedUsername()).isEqualTo("username");
assertThat(sender.transport.getConnectedPassword()).isEqualTo("password");
assertThat(sender.transport.isCloseCalled()).isTrue();
assertThat(sender.transport.getSentMessages()).hasSize(1);
assertThat(sender.transport.getSentMessage(0)).isEqualTo(mimeMessage);
assertThat(sender.transport.getSentMessages()).containsExactly(mimeMessage);
}
@Test
public void failedMailServerConnect() {
MockJavaMailSender sender = new MockJavaMailSender();
void failedMailServerConnect() {
sender.setHost(null);
sender.setUsername("username");
sender.setPassword("password");
SimpleMailMessage simpleMessage1 = new SimpleMailMessage();
assertThatExceptionOfType(MailSendException.class).isThrownBy(() ->
sender.send(simpleMessage1))
.satisfies(ex -> assertThat(ex.getFailedMessages()).containsExactly(entry(simpleMessage1, (Exception) ex.getCause())));
assertThatExceptionOfType(MailSendException.class)
.isThrownBy(() -> sender.send(simpleMessage1))
.satisfies(ex -> assertThat(ex.getFailedMessages()).containsExactly(entry(simpleMessage1, (Exception) ex.getCause())));
}
@Test
public void failedMailServerClose() {
MockJavaMailSender sender = new MockJavaMailSender();
void failedMailServerClose() {
sender.setHost("");
sender.setUsername("username");
sender.setPassword("password");
SimpleMailMessage simpleMessage1 = new SimpleMailMessage();
assertThatExceptionOfType(MailSendException.class).isThrownBy(() ->
sender.send(simpleMessage1))
assertThatExceptionOfType(MailSendException.class)
.isThrownBy(() -> sender.send(simpleMessage1))
.satisfies(ex -> assertThat(ex.getFailedMessages()).isEmpty());
}
@Test
public void failedSimpleMessage() throws MessagingException {
MockJavaMailSender sender = new MockJavaMailSender();
void failedSimpleMessage() throws Exception {
sender.setHost("host");
sender.setUsername("username");
sender.setPassword("password");
@@ -422,68 +380,60 @@ public class JavaMailSenderTests {
sender.send(simpleMessage1, simpleMessage2);
}
catch (MailSendException ex) {
ex.printStackTrace();
assertThat(sender.transport.getConnectedHost()).isEqualTo("host");
assertThat(sender.transport.getConnectedUsername()).isEqualTo("username");
assertThat(sender.transport.getConnectedPassword()).isEqualTo("password");
assertThat(sender.transport.isCloseCalled()).isTrue();
assertThat(sender.transport.getSentMessages()).hasSize(1);
assertThat(sender.transport.getSentMessage(0).getAllRecipients()[0]).isEqualTo(new InternetAddress("she@mail.org"));
assertThat(ex.getFailedMessages()).hasSize(1);
assertThat(ex.getFailedMessages().keySet().iterator().next()).isEqualTo(simpleMessage1);
Object subEx = ex.getFailedMessages().values().iterator().next();
boolean condition = subEx instanceof MessagingException;
assertThat(condition).isTrue();
assertThat(((MessagingException) subEx).getMessage()).isEqualTo("failed");
assertThat(ex.getFailedMessages().keySet()).containsExactly(simpleMessage1);
Exception subEx = ex.getFailedMessages().values().iterator().next();
assertThat(subEx).isInstanceOf(MessagingException.class).hasMessage("failed");
}
}
@Test
public void failedMimeMessage() throws MessagingException {
MockJavaMailSender sender = new MockJavaMailSender();
void failedMimeMessage() throws Exception {
sender.setHost("host");
sender.setUsername("username");
sender.setPassword("password");
MimeMessage mimeMessage1 = sender.createMimeMessage();
mimeMessage1.setRecipient(Message.RecipientType.TO, new InternetAddress("he@mail.org"));
mimeMessage1.setRecipient(RecipientType.TO, new InternetAddress("he@mail.org"));
mimeMessage1.setSubject("fail");
MimeMessage mimeMessage2 = sender.createMimeMessage();
mimeMessage2.setRecipient(Message.RecipientType.TO, new InternetAddress("she@mail.org"));
mimeMessage2.setRecipient(RecipientType.TO, new InternetAddress("she@mail.org"));
try {
sender.send(mimeMessage1, mimeMessage2);
}
catch (MailSendException ex) {
ex.printStackTrace();
assertThat(sender.transport.getConnectedHost()).isEqualTo("host");
assertThat(sender.transport.getConnectedUsername()).isEqualTo("username");
assertThat(sender.transport.getConnectedPassword()).isEqualTo("password");
assertThat(sender.transport.isCloseCalled()).isTrue();
assertThat(sender.transport.getSentMessages()).hasSize(1);
assertThat(sender.transport.getSentMessage(0)).isEqualTo(mimeMessage2);
assertThat(ex.getFailedMessages()).hasSize(1);
assertThat(ex.getFailedMessages().keySet().iterator().next()).isEqualTo(mimeMessage1);
Object subEx = ex.getFailedMessages().values().iterator().next();
boolean condition = subEx instanceof MessagingException;
assertThat(condition).isTrue();
assertThat(((MessagingException) subEx).getMessage()).isEqualTo("failed");
assertThat(sender.transport.getSentMessages()).containsExactly(mimeMessage2);
assertThat(ex.getFailedMessages().keySet()).containsExactly(mimeMessage1);
Exception subEx = ex.getFailedMessages().values().iterator().next();
assertThat(subEx).isInstanceOf(MessagingException.class).hasMessage("failed");
}
}
@Test
public void testConnection() throws MessagingException {
MockJavaMailSender sender = new MockJavaMailSender();
void testConnection() {
sender.setHost("host");
sender.testConnection();
assertThatNoException().isThrownBy(sender::testConnection);
}
@Test
public void testConnectionWithFailure() throws MessagingException {
MockJavaMailSender sender = new MockJavaMailSender();
void testConnectionWithFailure() {
sender.setHost(null);
assertThatExceptionOfType(MessagingException.class).isThrownBy(
sender::testConnection);
assertThatExceptionOfType(MessagingException.class).isThrownBy(sender::testConnection);
}
private static Stream<String> addresses(Address[] addresses) {
return Arrays.stream(addresses).map(InternetAddress.class::cast).map(InternetAddress::getAddress);
}
@@ -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.
@@ -165,14 +165,9 @@ public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperati
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AnnotationCacheOperationSource otherCos)) {
return false;
}
return (this.annotationParsers.equals(otherCos.annotationParsers) &&
this.publicMethodsOnly == otherCos.publicMethodsOnly);
return (this == other || (other instanceof AnnotationCacheOperationSource otherCos &&
this.annotationParsers.equals(otherCos.annotationParsers) &&
this.publicMethodsOnly == otherCos.publicMethodsOnly));
}
@Override
@@ -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.
@@ -19,37 +19,31 @@ package org.springframework.cache.interceptor;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
import org.springframework.lang.Nullable;
/**
* Advisor driven by a {@link CacheOperationSource}, used to include a
* cache advice bean for methods that are cacheable.
*
* @author Costin Leau
* @author Juergen Hoeller
* @since 3.1
* @see #setAdviceBeanName
* @see CacheInterceptor
*/
@SuppressWarnings("serial")
public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
@Nullable
private CacheOperationSource cacheOperationSource;
private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() {
@Override
@Nullable
protected CacheOperationSource getCacheOperationSource() {
return cacheOperationSource;
}
};
private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut();
/**
* Set the cache operation attribute source which is used to find cache
* attributes. This should usually be identical to the source reference
* set on the cache interceptor itself.
* @see CacheInterceptor#setCacheOperationSource
*/
public void setCacheOperationSource(CacheOperationSource cacheOperationSource) {
this.cacheOperationSource = cacheOperationSource;
this.pointcut.setCacheOperationSource(cacheOperationSource);
}
/**
@@ -35,28 +35,31 @@ import org.springframework.util.ObjectUtils;
* @since 3.1
*/
@SuppressWarnings("serial")
abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
protected CacheOperationSourcePointcut() {
@Nullable
private CacheOperationSource cacheOperationSource;
public CacheOperationSourcePointcut() {
setClassFilter(new CacheOperationSourceClassFilter());
}
public void setCacheOperationSource(@Nullable CacheOperationSource cacheOperationSource) {
this.cacheOperationSource = cacheOperationSource;
}
@Override
public boolean matches(Method method, Class<?> targetClass) {
CacheOperationSource cas = getCacheOperationSource();
return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
return (this.cacheOperationSource == null ||
!CollectionUtils.isEmpty(this.cacheOperationSource.getCacheOperations(method, targetClass)));
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof CacheOperationSourcePointcut otherPc)) {
return false;
}
return ObjectUtils.nullSafeEquals(getCacheOperationSource(), otherPc.getCacheOperationSource());
return (this == other || (other instanceof CacheOperationSourcePointcut otherPc &&
ObjectUtils.nullSafeEquals(this.cacheOperationSource, otherPc.cacheOperationSource)));
}
@Override
@@ -66,18 +69,10 @@ abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut
@Override
public String toString() {
return getClass().getName() + ": " + getCacheOperationSource();
return getClass().getName() + ": " + this.cacheOperationSource;
}
/**
* Obtain the underlying {@link CacheOperationSource} (may be {@code null}).
* To be implemented by subclasses.
*/
@Nullable
protected abstract CacheOperationSource getCacheOperationSource();
/**
* {@link ClassFilter} that delegates to {@link CacheOperationSource#isCandidateClass}
* for filtering classes whose methods are not worth searching to begin with.
@@ -89,8 +84,7 @@ abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut
if (CacheManager.class.isAssignableFrom(clazz)) {
return false;
}
CacheOperationSource cas = getCacheOperationSource();
return (cas == null || cas.isCandidateClass(clazz));
return (cacheOperationSource == null || cacheOperationSource.isCandidateClass(clazz));
}
}
@@ -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.
@@ -111,13 +111,8 @@ public class NameMatchCacheOperationSource implements CacheOperationSource, Seri
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof NameMatchCacheOperationSource otherTas)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.nameMap, otherTas.nameMap);
return (this == other || (other instanceof NameMatchCacheOperationSource otherCos &&
ObjectUtils.nullSafeEquals(this.nameMap, otherCos.nameMap)));
}
@Override
@@ -129,4 +124,5 @@ public class NameMatchCacheOperationSource implements CacheOperationSource, Seri
public String toString() {
return getClass().getName() + ": " + this.nameMap;
}
}
@@ -255,6 +255,7 @@ public class AnnotatedBeanDefinitionReader {
return;
}
abd.setAttribute(ConfigurationClassUtils.CANDIDATE_ATTRIBUTE, Boolean.TRUE);
abd.setInstanceSupplier(supplier);
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
@@ -56,6 +56,17 @@ public abstract class ConfigurationClassUtils {
static final String CONFIGURATION_CLASS_LITE = "lite";
/**
* When set to {@link Boolean#TRUE}, this attribute signals that the bean class
* for the given {@link BeanDefinition} should be considered as a candidate
* configuration class in 'lite' mode by default.
* <p>For example, a class registered directly with an {@code ApplicationContext}
* should always be considered a configuration class candidate.
* @since 6.0.10
*/
static final String CANDIDATE_ATTRIBUTE =
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "candidate");
static final String CONFIGURATION_CLASS_ATTRIBUTE =
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
@@ -136,7 +147,8 @@ public abstract class ConfigurationClassUtils {
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
else if (config != null || isConfigurationCandidate(metadata)) {
else if (config != null || Boolean.TRUE.equals(beanDef.getAttribute(CANDIDATE_ATTRIBUTE)) ||
isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
@@ -274,7 +274,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
handleAsyncError(ex);
}
else if (event != null) {
publishEvent(event);
publishEvents(event);
}
});
}
@@ -466,6 +466,9 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
}
/**
* Inner class to avoid a hard dependency on the Reactive Streams API at runtime.
*/
private class ReactiveResultHandler {
public boolean subscribeToPublisher(Object result) {
@@ -479,6 +482,9 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
}
/**
* Reactive Streams Subscriber for publishing follow-up events.
*/
private class EventPublicationSubscriber implements Subscriber<Object> {
@Override
@@ -463,7 +463,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
@Override
public void setApplicationStartup(ApplicationStartup applicationStartup) {
Assert.notNull(applicationStartup, "applicationStartup must not be null");
Assert.notNull(applicationStartup, "ApplicationStartup must not be null");
this.applicationStartup = applicationStartup;
}
@@ -946,7 +946,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
* onRefresh() method and publishing the
* {@link org.springframework.context.event.ContextRefreshedEvent}.
*/
@SuppressWarnings("deprecation")
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
@@ -1047,7 +1046,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
* @see #close()
* @see #registerShutdownHook()
*/
@SuppressWarnings("deprecation")
protected void doClose() {
// Check whether an actual close attempt is necessary...
if (this.active.get() && this.closed.compareAndSet(false, true)) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ import org.springframework.lang.Nullable;
* of a given task.
*
* @author Juergen Hoeller
* @author Arjen Poutsma
* @since 3.0
*/
public interface TriggerContext {
@@ -78,6 +79,7 @@ public interface TriggerContext {
/**
* Return the last <i>actual</i> execution time of the task,
* or {@code null} if not scheduled before.
* @since 6.0
*/
@Nullable
Instant lastActualExecution();
@@ -98,6 +100,7 @@ public interface TriggerContext {
/**
* Return the last completion time of the task,
* or {@code null} if not scheduled before.
* @since 6.0
*/
@Nullable
Instant lastCompletion();
@@ -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.
@@ -122,9 +122,12 @@ public @interface Scheduled {
* last invocation and the start of the next.
* <p>The time unit is milliseconds by default but can be overridden via
* {@link #timeUnit}.
* <p>This attribute variant supports Spring-style "${...}" placeholders
* as well as SpEL expressions.
* @return the delay as a String value &mdash; for example, a placeholder
* or a {@link java.time.Duration#parse java.time.Duration} compliant value
* @since 3.2.2
* @see #fixedDelay()
*/
String fixedDelayString() default "";
@@ -140,9 +143,12 @@ public @interface Scheduled {
* Execute the annotated method with a fixed period between invocations.
* <p>The time unit is milliseconds by default but can be overridden via
* {@link #timeUnit}.
* <p>This attribute variant supports Spring-style "${...}" placeholders
* as well as SpEL expressions.
* @return the period as a String value &mdash; for example, a placeholder
* or a {@link java.time.Duration#parse java.time.Duration} compliant value
* @since 3.2.2
* @see #fixedRate()
*/
String fixedRateString() default "";
@@ -161,9 +167,12 @@ public @interface Scheduled {
* {@link #fixedRate} or {@link #fixedDelay} task.
* <p>The time unit is milliseconds by default but can be overridden via
* {@link #timeUnit}.
* <p>This attribute variant supports Spring-style "${...}" placeholders
* as well as SpEL expressions.
* @return the initial delay as a String value &mdash; for example, a placeholder
* or a {@link java.time.Duration#parse java.time.Duration} compliant value
* @since 3.2.2
* @see #initialDelay()
*/
String initialDelayString() default "";
@@ -211,7 +211,7 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
public ScheduledFuture<?> schedule(Runnable task, Instant startTime) {
Duration initialDelay = Duration.between(this.clock.instant(), startTime);
try {
return this.scheduledExecutor.schedule(decorateTask(task, false), initialDelay.toMillis(), TimeUnit.MILLISECONDS);
return this.scheduledExecutor.schedule(decorateTask(task, false), initialDelay.toNanos(), TimeUnit.NANOSECONDS);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
@@ -222,7 +222,7 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) {
Duration initialDelay = Duration.between(this.clock.instant(), startTime);
try {
return this.scheduledExecutor.scheduleAtFixedRate(decorateTask(task, true), initialDelay.toMillis(), period.toMillis(), TimeUnit.MILLISECONDS);
return this.scheduledExecutor.scheduleAtFixedRate(decorateTask(task, true), initialDelay.toNanos(), period.toNanos(), TimeUnit.NANOSECONDS);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
@@ -232,7 +232,7 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period) {
try {
return this.scheduledExecutor.scheduleAtFixedRate(decorateTask(task, true), 0, period.toMillis(), TimeUnit.MILLISECONDS);
return this.scheduledExecutor.scheduleAtFixedRate(decorateTask(task, true), 0, period.toNanos(), TimeUnit.NANOSECONDS);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
@@ -243,7 +243,7 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay) {
Duration initialDelay = Duration.between(this.clock.instant(), startTime);
try {
return this.scheduledExecutor.scheduleWithFixedDelay(decorateTask(task, true), initialDelay.toMillis(), delay.toMillis(), TimeUnit.MILLISECONDS);
return this.scheduledExecutor.scheduleWithFixedDelay(decorateTask(task, true), initialDelay.toNanos(), delay.toNanos(), TimeUnit.NANOSECONDS);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
@@ -253,7 +253,7 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay) {
try {
return this.scheduledExecutor.scheduleWithFixedDelay(decorateTask(task, true), 0, delay.toMillis(), TimeUnit.MILLISECONDS);
return this.scheduledExecutor.scheduleWithFixedDelay(decorateTask(task, true), 0, delay.toNanos(), TimeUnit.NANOSECONDS);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
@@ -36,7 +36,10 @@ import org.springframework.lang.Nullable;
* Base class for setting up a {@link java.util.concurrent.ExecutorService}
* (typically a {@link java.util.concurrent.ThreadPoolExecutor} or
* {@link java.util.concurrent.ScheduledThreadPoolExecutor}).
* Defines common configuration settings and common lifecycle handling.
*
* <p>Defines common configuration settings and common lifecycle handling,
* inheriting thread customization options (name, priority, etc) from
* {@link org.springframework.util.CustomizableThreadCreator}.
*
* @author Juergen Hoeller
* @since 3.0
@@ -199,8 +202,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
/**
* Calls {@code shutdown} when the BeanFactory destroys
* the task executor instance.
* Calls {@code shutdown} when the BeanFactory destroys the executor instance.
* @see #shutdown()
*/
@Override
@@ -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.
@@ -80,7 +80,7 @@ class ReschedulingRunnable extends DelegatingErrorHandlingRunnable implements Sc
return null;
}
Duration initialDelay = Duration.between(this.triggerContext.getClock().instant(), this.scheduledExecutionTime);
this.currentFuture = this.executor.schedule(this, initialDelay.toMillis(), TimeUnit.MILLISECONDS);
this.currentFuture = this.executor.schedule(this, initialDelay.toNanos(), TimeUnit.NANOSECONDS);
return this;
}
}
@@ -158,8 +158,8 @@ class ReschedulingRunnable extends DelegatingErrorHandlingRunnable implements Sc
if (this == other) {
return 0;
}
long diff = getDelay(TimeUnit.MILLISECONDS) - other.getDelay(TimeUnit.MILLISECONDS);
return (diff == 0 ? 0 : ((diff < 0)? -1 : 1));
long diff = getDelay(TimeUnit.NANOSECONDS) - other.getDelay(TimeUnit.NANOSECONDS);
return (diff == 0 ? 0 : (diff < 0 ? -1 : 1));
}
}
@@ -382,7 +382,7 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
ScheduledExecutorService executor = getScheduledExecutor();
Duration initialDelay = Duration.between(this.clock.instant(), startTime);
try {
return executor.schedule(errorHandlingTask(task, false), initialDelay.toMillis(), TimeUnit.MILLISECONDS);
return executor.schedule(errorHandlingTask(task, false), initialDelay.toNanos(), TimeUnit.NANOSECONDS);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
@@ -394,7 +394,7 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
ScheduledExecutorService executor = getScheduledExecutor();
Duration initialDelay = Duration.between(this.clock.instant(), startTime);
try {
return executor.scheduleAtFixedRate(errorHandlingTask(task, true), initialDelay.toMillis(), period.toMillis(), TimeUnit.MILLISECONDS);
return executor.scheduleAtFixedRate(errorHandlingTask(task, true), initialDelay.toNanos(), period.toNanos(), TimeUnit.NANOSECONDS);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
@@ -405,7 +405,7 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period) {
ScheduledExecutorService executor = getScheduledExecutor();
try {
return executor.scheduleAtFixedRate(errorHandlingTask(task, true), 0, period.toMillis(), TimeUnit.MILLISECONDS);
return executor.scheduleAtFixedRate(errorHandlingTask(task, true), 0, period.toNanos(), TimeUnit.NANOSECONDS);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
@@ -417,7 +417,7 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
ScheduledExecutorService executor = getScheduledExecutor();
Duration initialDelay = Duration.between(this.clock.instant(), startTime);
try {
return executor.scheduleWithFixedDelay(errorHandlingTask(task, true), initialDelay.toMillis(), delay.toMillis(), TimeUnit.MILLISECONDS);
return executor.scheduleWithFixedDelay(errorHandlingTask(task, true), initialDelay.toNanos(), delay.toNanos(), TimeUnit.NANOSECONDS);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
@@ -428,7 +428,7 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay) {
ScheduledExecutorService executor = getScheduledExecutor();
try {
return executor.scheduleWithFixedDelay(errorHandlingTask(task, true), 0, delay.toMillis(), TimeUnit.MILLISECONDS);
return executor.scheduleWithFixedDelay(errorHandlingTask(task, true), 0, delay.toNanos(), TimeUnit.NANOSECONDS);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
@@ -485,7 +485,7 @@ public class ScheduledTaskRegistrar implements ScheduledTaskHolder, Initializing
}
if (this.taskScheduler != null) {
Duration initialDelay = task.getInitialDelayDuration();
if (initialDelay.toMillis() > 0) {
if (initialDelay.toNanos() > 0) {
Instant startTime = this.taskScheduler.getClock().instant().plus(initialDelay);
scheduledTask.future =
this.taskScheduler.scheduleAtFixedRate(task.getRunnable(), startTime, task.getIntervalDuration());
@@ -57,18 +57,12 @@ public final class CronExpression {
private final String expression;
private CronExpression(
CronField seconds,
CronField minutes,
CronField hours,
CronField daysOfMonth,
CronField months,
CronField daysOfWeek,
String expression) {
private CronExpression(CronField seconds, CronField minutes, CronField hours,
CronField daysOfMonth, CronField months, CronField daysOfWeek, String expression) {
// reverse order, to make big changes first
// to make sure we end up at 0 nanos, we add an extra field
this.fields = new CronField[]{daysOfWeek, months, daysOfMonth, hours, minutes, seconds, CronField.zeroNanos()};
// Reverse order, to make big changes first.
// To make sure we end up at 0 nanos, we add an extra field.
this.fields = new CronField[] {daysOfWeek, months, daysOfMonth, hours, minutes, seconds, CronField.zeroNanos()};
this.expression = expression;
}
@@ -267,27 +261,19 @@ public final class CronExpression {
}
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof CronExpression that &&
Arrays.equals(this.fields, that.fields)));
}
@Override
public int hashCode() {
return Arrays.hashCode(this.fields);
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o instanceof CronExpression other) {
return Arrays.equals(this.fields, other.fields);
}
else {
return false;
}
}
/**
* Return the expression string used to create this {@code CronExpression}.
* @return the expression string
*/
@Override
public String toString() {
@@ -89,7 +89,7 @@ public class CronTrigger implements Trigger {
/**
* Determine the next execution time according to the given trigger context.
* <p>Next execution times are calculated based on the
* {@linkplain TriggerContext#lastCompletionTime completion time} of the
* {@linkplain TriggerContext#lastCompletion completion time} of the
* previous execution; therefore, overlapping executions won't occur.
*/
@Override
@@ -114,8 +114,9 @@ public class CronTrigger implements Trigger {
@Override
public boolean equals(@Nullable Object obj) {
return (this == obj || (obj instanceof CronTrigger that && this.expression.equals(that.expression)));
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof CronTrigger that &&
this.expression.equals(that.expression)));
}
@Override
@@ -29,8 +29,9 @@ import org.springframework.util.Assert;
/**
* Extension of {@link CronField} for
* <a href="https://www.quartz-scheduler.org>Quartz</a> -specific fields.
* Created using the {@code parse*} methods, uses a {@link TemporalAdjuster}
* <a href="https://www.quartz-scheduler.org">Quartz</a>-specific fields.
*
* <p>Created using the {@code parse*} methods, uses a {@link TemporalAdjuster}
* internally.
*
* @author Arjen Poutsma
@@ -68,8 +69,9 @@ final class QuartzCronField extends CronField {
}
/**
* Parse the given value into a days of months {@code QuartzCronField}, the fourth entry of a cron expression.
* Expects a "L" or "W" in the given value.
* Parse the given value into a days of months {@code QuartzCronField},
* the fourth entry of a cron expression.
* <p>Expects a "L" or "W" in the given value.
*/
public static QuartzCronField parseDaysOfMonth(String value) {
int idx = value.lastIndexOf('L');
@@ -121,8 +123,9 @@ final class QuartzCronField extends CronField {
}
/**
* Parse the given value into a days of week {@code QuartzCronField}, the sixth entry of a cron expression.
* Expects a "L" or "#" in the given value.
* Parse the given value into a days of week {@code QuartzCronField},
* the sixth entry of a cron expression.
* <p>Expects a "L" or "#" in the given value.
*/
public static QuartzCronField parseDaysOfWeek(String value) {
int idx = value.lastIndexOf('L');
@@ -227,7 +230,7 @@ final class QuartzCronField extends CronField {
}
/**
* Return a temporal adjuster that finds the nth-to-last day of the month.
* Returns a temporal adjuster that finds the nth-to-last day of the month.
* @param offset the negative offset, i.e. -3 means third-to-last
* @return a nth-to-last day-of-month adjuster
*/
@@ -241,7 +244,7 @@ final class QuartzCronField extends CronField {
}
/**
* Return a temporal adjuster that finds the weekday nearest to the given
* Returns a temporal adjuster that finds the weekday nearest to the given
* day-of-month. If {@code dayOfMonth} falls on a Saturday, the date is
* moved back to Friday; if it falls on a Sunday (or if {@code dayOfMonth}
* is 1 and it falls on a Saturday), it is moved forward to Monday.
@@ -292,7 +295,7 @@ final class QuartzCronField extends CronField {
}
/**
* Return a temporal adjuster that finds the last of the given doy-of-week
* Returns a temporal adjuster that finds the last of the given day-of-week
* in a month.
*/
private static TemporalAdjuster lastInMonth(DayOfWeek dayOfWeek) {
@@ -54,7 +54,7 @@ import org.springframework.util.ClassUtils;
*/
class BeanValidationBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
private static final boolean isBeanValidationPresent = ClassUtils.isPresent(
private static final boolean beanValidationPresent = ClassUtils.isPresent(
"jakarta.validation.Validation", BeanValidationBeanRegistrationAotProcessor.class.getClassLoader());
private static final Log logger = LogFactory.getLog(BeanValidationBeanRegistrationAotProcessor.class);
@@ -63,13 +63,16 @@ class BeanValidationBeanRegistrationAotProcessor implements BeanRegistrationAotP
@Override
@Nullable
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
if (isBeanValidationPresent) {
if (beanValidationPresent) {
return BeanValidationDelegate.processAheadOfTime(registeredBean);
}
return null;
}
/**
* Inner class to avoid a hard dependency on the Bean Validation API at runtime.
*/
private static class BeanValidationDelegate {
@Nullable
@@ -28,6 +28,7 @@ import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.aop.testfixture.advice.CountingBeforeAdvice;
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
import org.springframework.beans.testfixture.beans.ITestBean;
@@ -35,6 +36,7 @@ import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
@@ -304,6 +306,8 @@ public class CglibProxyTests extends AbstractAopProxyTests implements Serializab
CglibAopProxy cglib = new CglibAopProxy(as);
ITestBean proxy1 = (ITestBean) cglib.getProxy();
ITestBean proxy1a = (ITestBean) cglib.getProxy();
assertThat(proxy1a.getClass()).isSameAs(proxy1.getClass());
mockTargetSource.setTarget(proxy1);
as = new AdvisedSupport(new Class<?>[]{});
@@ -313,6 +317,39 @@ public class CglibProxyTests extends AbstractAopProxyTests implements Serializab
ITestBean proxy2 = (ITestBean) cglib.getProxy();
assertThat(proxy2).isInstanceOf(Serializable.class);
assertThat(proxy2.getClass()).isNotSameAs(proxy1.getClass());
ITestBean proxy2a = (ITestBean) cglib.getProxy();
assertThat(proxy2a).isInstanceOf(Serializable.class);
assertThat(proxy2a.getClass()).isSameAs(proxy2.getClass());
mockTargetSource.setTarget(proxy1);
as = new AdvisedSupport(new Class<?>[]{});
as.setTargetSource(mockTargetSource);
as.addAdvisor(new DefaultPointcutAdvisor(new AnnotationMatchingPointcut(Nullable.class), new NopInterceptor()));
cglib = new CglibAopProxy(as);
ITestBean proxy3 = (ITestBean) cglib.getProxy();
assertThat(proxy3).isInstanceOf(Serializable.class);
assertThat(proxy3.getClass()).isNotSameAs(proxy2.getClass());
ITestBean proxy3a = (ITestBean) cglib.getProxy();
assertThat(proxy3a).isInstanceOf(Serializable.class);
assertThat(proxy3a.getClass()).isSameAs(proxy3.getClass());
mockTargetSource.setTarget(proxy1);
as = new AdvisedSupport(new Class<?>[]{});
as.setTargetSource(mockTargetSource);
as.addAdvisor(new DefaultPointcutAdvisor(new AnnotationMatchingPointcut(NonNull.class), new NopInterceptor()));
cglib = new CglibAopProxy(as);
ITestBean proxy4 = (ITestBean) cglib.getProxy();
assertThat(proxy4).isInstanceOf(Serializable.class);
assertThat(proxy4.getClass()).isNotSameAs(proxy3.getClass());
ITestBean proxy4a = (ITestBean) cglib.getProxy();
assertThat(proxy4a).isInstanceOf(Serializable.class);
assertThat(proxy4a.getClass()).isSameAs(proxy4.getClass());
}
@Test
@@ -0,0 +1,109 @@
/*
* 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.context.annotation;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link Bean @Bean} 'lite' mode features that are not covered
* elsewhere in the test suite.
*
* @author Sam Brannen
* @since 6.0.10
* @see ConfigurationClassPostProcessorTests
*/
class BeanLiteModeTests {
@Test
void beanMethodsAreFoundWhenInheritedAsInterfaceDefaultMethods() {
assertBeansAreFound(InterfaceDefaultMethodsConfig.class);
}
@Test
void beanMethodsAreFoundWhenDeclaredLocally() {
assertBeansAreFound(BaseConfig.class);
}
@Test
void beanMethodsAreFoundWhenDeclaredLocallyAndInSuperclass() {
assertBeansAreFound(OverridingConfig.class, "foo", "xyz");
}
@Test // gh-30449
void beanMethodsAreFoundWhenDeclaredOnlyInSuperclass() {
assertBeansAreFound(ExtendedConfig.class, "foo", "xyz");
}
private static void assertBeansAreFound(Class<?> configClass) {
assertBeansAreFound(configClass, "foo", "bar");
}
private static void assertBeansAreFound(Class<?> configClass, String expected1, String expected2) {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configClass)) {
String bean1 = context.getBean("bean1", String.class);
String bean2 = context.getBean("bean2", String.class);
assertThat(bean1).isEqualTo(expected1);
assertThat(bean2).isEqualTo(expected2);
}
}
interface ConfigInterface {
@Bean
default String bean1() {
return "foo";
}
@Bean
default String bean2() {
return "bar";
}
}
static class InterfaceDefaultMethodsConfig implements ConfigInterface {
}
static class BaseConfig {
@Bean
String bean1() {
return "foo";
}
@Bean
String bean2() {
return "bar";
}
}
static class OverridingConfig extends BaseConfig {
@Bean
@Override
String bean2() {
return "xyz";
}
}
static class ExtendedConfig extends OverridingConfig {
}
}
@@ -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.
@@ -41,6 +41,7 @@ class ImportTests {
private DefaultListableBeanFactory processConfigurationClasses(Class<?>... classes) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.setAllowBeanDefinitionOverriding(false);
for (Class<?> clazz : classes) {
beanFactory.registerBeanDefinition(clazz.getSimpleName(), new RootBeanDefinition(clazz));
}
@@ -56,9 +57,10 @@ class ImportTests {
for (Class<?> clazz : classes) {
beanFactory.getBean(clazz);
}
}
// ------------------------------------------------------------------------
@Test
void testProcessImportsWithAsm() {
int configClasses = 2;
@@ -158,6 +160,13 @@ class ImportTests {
assertBeanDefinitionCount(configClasses + beansInClasses, FirstLevel.class);
}
@Test
void testImportAnnotationWithThreeLevelRecursionAndDoubleImport() {
int configClasses = 5;
int beansInClasses = 5;
assertBeanDefinitionCount(configClasses + beansInClasses, FirstLevel.class, FirstLevelPlus.class);
}
// ------------------------------------------------------------------------
@Test
@@ -167,7 +176,6 @@ class ImportTests {
assertBeanDefinitionCount((configClasses + beansInClasses), WithMultipleArgumentsToImportAnnotation.class);
}
@Test
void testImportAnnotationWithMultipleArgumentsResultingInOverriddenBeanDefinition() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
@@ -245,6 +253,11 @@ class ImportTests {
}
}
@Configuration
@Import(ThirdLevel.class)
static class FirstLevelPlus {
}
@Configuration
@Import({ThirdLevel.class, InitBean.class})
static class SecondLevel {
@@ -76,11 +76,13 @@ class GenericApplicationContextTests {
private final GenericApplicationContext context = new GenericApplicationContext();
@AfterEach
void closeContext() {
context.close();
}
@Test
void getBeanForClass() {
context.registerBeanDefinition("testBean", new RootBeanDefinition(String.class));
@@ -91,7 +93,7 @@ class GenericApplicationContextTests {
assertThat(context.getBean(CharSequence.class)).isSameAs(context.getBean("testBean"));
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)
.isThrownBy(() -> context.getBean(Object.class));
.isThrownBy(() -> context.getBean(Object.class));
}
@Test
@@ -509,6 +511,7 @@ class GenericApplicationContextTests {
context.close();
}
private MergedBeanDefinitionPostProcessor registerMockMergedBeanDefinitionPostProcessor(GenericApplicationContext context) {
MergedBeanDefinitionPostProcessor bpp = mock();
context.registerBeanDefinition("bpp", BeanDefinitionBuilder.rootBeanDefinition(
@@ -563,9 +566,9 @@ class GenericApplicationContextTests {
public void setCounter(Integer counter) {
this.counter = counter;
}
}
static class TestAotFactoryBean<T> extends AbstractFactoryBean<T> {
TestAotFactoryBean() {
@@ -584,6 +587,7 @@ class GenericApplicationContextTests {
}
}
static class PingPongProtocolResolver implements ProtocolResolver {
@Override
@@ -24,6 +24,7 @@ import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class QualifierConfiguration {
@SuppressWarnings("unused")
private String bean;
@Autowired
@@ -44,7 +45,7 @@ public class QualifierConfiguration {
@Bean
@Qualifier("2")
public String two() {
return "one";
return "two";
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,11 +19,14 @@ package org.springframework.aot;
import org.springframework.core.NativeDetector;
import org.springframework.core.SpringProperties;
import static org.springframework.core.NativeDetector.Context;
/**
* Utility for determining if AOT-processed optimizations must be used rather
* than the regular runtime. Strictly for internal use within the framework.
*
* @author Stephane Nicoll
* @author Sebastien Deleuze
* @since 6.0
*/
public abstract class AotDetector {
@@ -36,6 +39,9 @@ public abstract class AotDetector {
*/
public static final String AOT_ENABLED = "spring.aot.enabled";
private static final boolean inNativeImage = NativeDetector.inNativeImage(Context.RUN, Context.BUILD);
/**
* Determine whether AOT optimizations must be considered at runtime. This
* is mandatory in a native image but can be triggered on the JVM using
@@ -43,7 +49,7 @@ public abstract class AotDetector {
* @return whether AOT optimizations must be considered
*/
public static boolean useGeneratedArtifacts() {
return (NativeDetector.inNativeImage() || SpringProperties.getFlag(AOT_ENABLED));
return (inNativeImage || SpringProperties.getFlag(AOT_ENABLED));
}
}
@@ -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.
@@ -22,7 +22,7 @@ import java.lang.reflect.Member;
import java.lang.reflect.Method;
/**
* Represent predefined {@linkplain Member members} groups.
* Predefined {@link Member} categories.
*
* @author Andy Clement
* @author Sebastien Deleuze
@@ -39,14 +39,14 @@ public enum MemberCategory {
/**
* A category that represents {@linkplain Class#getDeclaredFields() declared
* fields}, that is all fields defined by the class, but not inherited ones.
* fields}: all fields defined by the class but not inherited fields.
* @see Class#getDeclaredFields()
*/
DECLARED_FIELDS,
/**
* A category that defines public {@linkplain Constructor constructors} can
* be introspected, but not invoked.
* be introspected but not invoked.
* @see Class#getConstructors()
* @see ExecutableMode#INTROSPECT
*/
@@ -54,7 +54,7 @@ public enum MemberCategory {
/**
* A category that defines {@linkplain Class#getDeclaredConstructors() all
* constructors} can be introspected, but not invoked.
* constructors} can be introspected but not invoked.
* @see Class#getDeclaredConstructors()
* @see ExecutableMode#INTROSPECT
*/
@@ -78,7 +78,7 @@ public enum MemberCategory {
/**
* A category that defines public {@linkplain Method methods}, including
* inherited ones can be introspect, but not invoked.
* inherited ones, can be introspected but not invoked.
* @see Class#getMethods()
* @see ExecutableMode#INTROSPECT
*/
@@ -86,7 +86,7 @@ public enum MemberCategory {
/**
* A category that defines {@linkplain Class#getDeclaredMethods() all
* methods}, excluding inherited ones can be introspected, but not invoked.
* methods}, excluding inherited ones, can be introspected but not invoked.
* @see Class#getDeclaredMethods()
* @see ExecutableMode#INTROSPECT
*/
@@ -94,7 +94,7 @@ public enum MemberCategory {
/**
* A category that defines public {@linkplain Method methods}, including
* inherited ones can be invoked.
* inherited ones, can be invoked.
* @see Class#getMethods()
* @see ExecutableMode#INVOKE
*/
@@ -102,7 +102,7 @@ public enum MemberCategory {
/**
* A category that defines {@linkplain Class#getDeclaredMethods() all
* methods}, excluding inherited ones can be invoked.
* methods}, excluding inherited ones, can be invoked.
* @see Class#getDeclaredMethods()
* @see ExecutableMode#INVOKE
*/
@@ -110,16 +110,18 @@ public enum MemberCategory {
/**
* A category that represents public {@linkplain Class#getClasses() inner
* classes}. Contrary to other categories, this does not register any
* particular reflection for them but rather make sure they are available
* classes}.
* <p>Contrary to other categories, this does not register any particular
* reflection for inner classes but rather makes sure they are available
* via a call to {@link Class#getClasses}.
*/
PUBLIC_CLASSES,
/**
* A category that represents all {@linkplain Class#getDeclaredClasses()
* inner classes}. Contrary to other categories, this does not register any
* particular reflection for them but rather make sure they are available
* inner classes}.
* <p>Contrary to other categories, this does not register any particular
* reflection for inner classes but rather makes sure they are available
* via a call to {@link Class#getDeclaredClasses}.
*/
DECLARED_CLASSES;
@@ -21,6 +21,8 @@ import java.util.Collections;
import java.util.List;
import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
@@ -37,15 +39,17 @@ class ObjectToObjectConverterRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
ReflectionHints reflectionHints = hints.reflection();
TypeReference sqlDateTypeReference = TypeReference.of("java.sql.Date");
hints.reflection().registerTypeIfPresent(classLoader, sqlDateTypeReference.getName(), hint -> hint
reflectionHints.registerTypeIfPresent(classLoader, sqlDateTypeReference.getName(), hint -> hint
.withMethod("toLocalDate", Collections.emptyList(), ExecutableMode.INVOKE)
.onReachableType(sqlDateTypeReference)
.withMethod("valueOf", List.of(TypeReference.of(LocalDate.class)), ExecutableMode.INVOKE)
.onReachableType(sqlDateTypeReference));
hints.reflection().registerTypeIfPresent(classLoader, "org.springframework.http.HttpMethod",
reflectionHints.registerTypeIfPresent(classLoader, "org.springframework.http.HttpMethod",
builder -> builder.withMethod("valueOf", List.of(TypeReference.of(String.class)), ExecutableMode.INVOKE));
reflectionHints.registerTypeIfPresent(classLoader, "java.net.URI", MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
}
}
@@ -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.
@@ -33,8 +33,9 @@ import org.graalvm.nativeimage.hosted.Feature;
class PreComputeFieldFeature implements Feature {
private static Pattern[] patterns = {
Pattern.compile(Pattern.quote("org.springframework.core.NativeDetector#imageCode")),
Pattern.compile(Pattern.quote("org.springframework.cglib.core.AbstractClassGenerator#imageCode")),
Pattern.compile(Pattern.quote("org.springframework.core.NativeDetector#inNativeImage")),
Pattern.compile(Pattern.quote("org.springframework.cglib.core.AbstractClassGenerator#inNativeImage")),
Pattern.compile(Pattern.quote("org.springframework.aot.AotDetector#inNativeImage")),
Pattern.compile(Pattern.quote("org.springframework.") + ".*#.*Present"),
Pattern.compile(Pattern.quote("org.springframework.") + ".*#.*PRESENT"),
Pattern.compile(Pattern.quote("reactor.") + ".*#.*Available"),
@@ -43,8 +43,12 @@ abstract public class AbstractClassGenerator<T> implements ClassGenerator {
private static final boolean DEFAULT_USE_CACHE =
Boolean.parseBoolean(System.getProperty("cglib.useCache", "true"));
// See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java
private static final boolean imageCode = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);
private static final boolean inNativeImage;
static {
String imageCode = System.getProperty("org.graalvm.nativeimage.imagecode");
inNativeImage = "buildtime".equals(imageCode) || "runtime".equals(imageCode);
}
private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE;
@@ -354,7 +358,7 @@ abstract public class AbstractClassGenerator<T> implements ClassGenerator {
}
}
// SPRING PATCH BEGIN
if (imageCode) {
if (inNativeImage) {
throw new UnsupportedOperationException("CGLIB runtime enhancement not supported on native image. " +
"Make sure to include a pre-generated class on the classpath instead: " + getClassName());
}
@@ -737,7 +737,6 @@ public class CodeEmitter extends LocalVariablesSorter {
* on the top of the stack with the unwrapped (primitive)
* equivalent. For example, Character -> char.
* @param type the class indicating the desired type of the top stack value
* @return true if the value was unboxed
*/
public void unbox(Type type) {
Type t = Constants.TYPE_NUMBER;
@@ -251,26 +251,17 @@ public class TypeUtils {
}
public static Type getBoxedType(Type type) {
switch (type.getSort()) {
case Type.CHAR:
return Constants.TYPE_CHARACTER;
case Type.BOOLEAN:
return Constants.TYPE_BOOLEAN;
case Type.DOUBLE:
return Constants.TYPE_DOUBLE;
case Type.FLOAT:
return Constants.TYPE_FLOAT;
case Type.LONG:
return Constants.TYPE_LONG;
case Type.INT:
return Constants.TYPE_INTEGER;
case Type.SHORT:
return Constants.TYPE_SHORT;
case Type.BYTE:
return Constants.TYPE_BYTE;
default:
return type;
}
return switch (type.getSort()) {
case Type.CHAR -> Constants.TYPE_CHARACTER;
case Type.BOOLEAN -> Constants.TYPE_BOOLEAN;
case Type.DOUBLE -> Constants.TYPE_DOUBLE;
case Type.FLOAT -> Constants.TYPE_FLOAT;
case Type.LONG -> Constants.TYPE_LONG;
case Type.INT -> Constants.TYPE_INTEGER;
case Type.SHORT -> Constants.TYPE_SHORT;
case Type.BYTE -> Constants.TYPE_BYTE;
default -> type;
};
}
public static Type getUnboxedType(Type type) {
@@ -307,13 +298,10 @@ public class TypeUtils {
}
public static boolean isPrimitive(Type type) {
switch (type.getSort()) {
case Type.ARRAY:
case Type.OBJECT:
return false;
default:
return true;
}
return switch (type.getSort()) {
case Type.ARRAY, Type.OBJECT -> false;
default -> true;
};
}
public static String emulateClassGetName(Type type) {
@@ -340,17 +328,17 @@ public class TypeUtils {
}
public static int ICONST(int value) {
switch (value) {
case -1: return Constants.ICONST_M1;
case 0: return Constants.ICONST_0;
case 1: return Constants.ICONST_1;
case 2: return Constants.ICONST_2;
case 3: return Constants.ICONST_3;
case 4: return Constants.ICONST_4;
case 5: return Constants.ICONST_5;
}
return -1; // error
}
return switch (value) {
case -1 -> Constants.ICONST_M1;
case 0 -> Constants.ICONST_0;
case 1 -> Constants.ICONST_1;
case 2 -> Constants.ICONST_2;
case 3 -> Constants.ICONST_3;
case 4 -> Constants.ICONST_4;
case 5 -> Constants.ICONST_5;
default -> -1; // error
};
}
public static int LCONST(long value) {
if (value == 0L) {
@@ -385,43 +373,33 @@ public class TypeUtils {
}
public static int NEWARRAY(Type type) {
switch (type.getSort()) {
case Type.BYTE:
return Constants.T_BYTE;
case Type.CHAR:
return Constants.T_CHAR;
case Type.DOUBLE:
return Constants.T_DOUBLE;
case Type.FLOAT:
return Constants.T_FLOAT;
case Type.INT:
return Constants.T_INT;
case Type.LONG:
return Constants.T_LONG;
case Type.SHORT:
return Constants.T_SHORT;
case Type.BOOLEAN:
return Constants.T_BOOLEAN;
default:
return -1; // error
}
return switch (type.getSort()) {
case Type.BYTE -> Constants.T_BYTE;
case Type.CHAR -> Constants.T_CHAR;
case Type.DOUBLE -> Constants.T_DOUBLE;
case Type.FLOAT -> Constants.T_FLOAT;
case Type.INT -> Constants.T_INT;
case Type.LONG -> Constants.T_LONG;
case Type.SHORT -> Constants.T_SHORT;
case Type.BOOLEAN -> Constants.T_BOOLEAN;
default -> -1; // error
};
}
public static String escapeType(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0, len = s.length(); i < len; i++) {
char c = s.charAt(i);
switch (c) {
case '$': sb.append("$24"); break;
case '.': sb.append("$2E"); break;
case '[': sb.append("$5B"); break;
case ';': sb.append("$3B"); break;
case '(': sb.append("$28"); break;
case ')': sb.append("$29"); break;
case '/': sb.append("$2F"); break;
default:
sb.append(c);
}
switch (c) {
case '$' -> sb.append("$24");
case '.' -> sb.append("$2E");
case '[' -> sb.append("$5B");
case ';' -> sb.append("$3B");
case '(' -> sb.append("$28");
case ')' -> sb.append("$29");
case '/' -> sb.append("$2F");
default -> sb.append(c);
}
}
return sb.toString();
}
@@ -74,7 +74,7 @@ public class InterfaceMaker extends AbstractClassGenerator
* Add all the public methods in the specified class.
* Methods from superclasses are included, except for methods declared in the base
* Object class (e.g. <code>getClass</code>, <code>equals</code>, <code>hashCode</code>).
* @param class the class containing the methods to add to the interface
* @param clazz the class containing the methods to add to the interface
*/
public void add(Class clazz) {
Method[] methods = clazz.getMethods();
@@ -28,7 +28,7 @@ public interface InvocationHandler
extends Callback
{
/**
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
@@ -25,7 +25,7 @@ import org.springframework.cglib.transform.MethodFilterTransformer;
import org.springframework.cglib.transform.TransformingClassGenerator;
/**
* A {@link GeneratorStrategy} suitable for use with {@link org.springframework.cglib.Enhancer} which
* A {@link GeneratorStrategy} suitable for use with {@link org.springframework.cglib.proxy.Enhancer} which
* causes all undeclared exceptions thrown from within a proxied method to be wrapped
* in an alternative exception of your choice.
*/
@@ -64,7 +64,6 @@ abstract public class ParallelSorter extends SorterTemplate {
* @param arrays An array of arrays to sort. The arrays may be a mix
* of primitive and non-primitive types, but should all be the same
* length.
* @param loader ClassLoader for generated class, uses "current" if null
*/
public static ParallelSorter create(Object[] arrays) {
Generator gen = new Generator();
@@ -135,8 +134,7 @@ abstract public class ParallelSorter extends SorterTemplate {
/**
* Sort the arrays using an in-place merge sort.
* @param index array (column) to sort by
* @param lo starting array index (row), inclusive
* @param hi ending array index (row), exclusive
* @param cmp Comparator to use if the specified column is non-primitive
*/
public void mergeSort(int index, Comparator cmp) {
mergeSort(index, 0, len(), cmp);
@@ -179,7 +179,7 @@ public final class CollectionFactory {
@SuppressWarnings("unchecked")
public static <E> Collection<E> createCollection(Class<?> collectionType, @Nullable Class<?> elementType, int capacity) {
Assert.notNull(collectionType, "Collection type must not be null");
if (LinkedHashSet.class == collectionType || HashSet.class == collectionType ||
if (LinkedHashSet.class == collectionType ||
Set.class == collectionType || Collection.class == collectionType) {
return new LinkedHashSet<>(capacity);
}
@@ -197,6 +197,9 @@ public final class CollectionFactory {
Assert.notNull(elementType, "Cannot create EnumSet for unknown element type");
return EnumSet.noneOf(asEnumType(elementType));
}
else if (HashSet.class == collectionType) {
return new HashSet<>(capacity);
}
else {
if (collectionType.isInterface() || !Collection.class.isAssignableFrom(collectionType)) {
throw new IllegalArgumentException("Unsupported Collection type: " + collectionType.getName());
@@ -297,7 +300,7 @@ public final class CollectionFactory {
@SuppressWarnings({"rawtypes", "unchecked"})
public static <K, V> Map<K, V> createMap(Class<?> mapType, @Nullable Class<?> keyType, int capacity) {
Assert.notNull(mapType, "Map type must not be null");
if (LinkedHashMap.class == mapType || HashMap.class == mapType || Map.class == mapType) {
if (LinkedHashMap.class == mapType || Map.class == mapType) {
return new LinkedHashMap<>(capacity);
}
else if (LinkedMultiValueMap.class == mapType || MultiValueMap.class == mapType) {
@@ -310,6 +313,9 @@ public final class CollectionFactory {
Assert.notNull(keyType, "Cannot create EnumMap for unknown key type");
return new EnumMap(asEnumType(keyType));
}
else if (HashMap.class == mapType) {
return new HashMap<>(capacity);
}
else {
if (mapType.isInterface() || !Map.class.isAssignableFrom(mapType)) {
throw new IllegalArgumentException("Unsupported Map type: " + mapType.getName());
@@ -780,6 +780,7 @@ public class MethodParameter {
return new MethodParameter(this);
}
/**
* Create a new MethodParameter for the given method or constructor.
* <p>This is a convenience factory method for scenarios where a

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