mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
121 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0abadaa2f6 | |||
| b9221656cc | |||
| c30f6aa427 | |||
| aa2028127f | |||
| a82659c837 | |||
| 3415b04c73 | |||
| a73ad52a8a | |||
| 13c32d80ba | |||
| 3171a8b0e2 | |||
| 927d27b121 | |||
| 1df5e9f30c | |||
| ed74b04520 | |||
| 6cfb11a360 | |||
| 3b263c5c3d | |||
| f18a85e193 | |||
| f7d195782f | |||
| bbab4faf7a | |||
| 3fb98b6a97 | |||
| dc4f46df9f | |||
| 80af7bedef | |||
| f8c8873c99 | |||
| cc8c852c2b | |||
| fe7f8e2de5 | |||
| 0a5aff1b60 | |||
| 3d61d9e0d8 | |||
| 1a26e17f41 | |||
| 439bcd6715 | |||
| 4e33d0c293 | |||
| 9b4e0e9837 | |||
| 071d6a2a5a | |||
| d7d0292654 | |||
| 2817dce78f | |||
| 698acd32a7 | |||
| f36327b380 | |||
| ea9c0ae6c0 | |||
| c16f582ed8 | |||
| 6931106c5e | |||
| 045df81f14 | |||
| e210f08dce | |||
| 2317bef021 | |||
| ce5189a0a0 | |||
| 7a4ed38cd4 | |||
| c1fe57135e | |||
| 2c8d1b7bff | |||
| bd63c35b0b | |||
| c052a02592 | |||
| 5ad853ef5b | |||
| dfbed616ba | |||
| 94214562d0 | |||
| be94e1a2fb | |||
| f2ae106c32 | |||
| 7b20aefecf | |||
| cdc4497664 | |||
| 6cc084dbde | |||
| 974e10379a | |||
| 22ef48bec2 | |||
| 4be813bbee | |||
| 65617f1e75 | |||
| aaebf5749c | |||
| 24fa8793b1 | |||
| 4f10f559f7 | |||
| 2f35e7756b | |||
| cc50af08e4 | |||
| d08e4081c2 | |||
| 9751987dc1 | |||
| d2906253f1 | |||
| 4b8adf2dcc | |||
| c68552556f | |||
| b738a20233 | |||
| cca8fbd3de | |||
| 21397a67c6 | |||
| e8ab53e76d | |||
| 5b471a5349 | |||
| 08240bbcac | |||
| d719f3fc06 | |||
| 938dff7bbe | |||
| 162ccdd155 | |||
| f1594312cd | |||
| 7629ea5672 | |||
| 454a85978f | |||
| 2cbc7eed73 | |||
| 7a05f81844 | |||
| 71b2154ec8 | |||
| a716391aab | |||
| 09b2e5d0fb | |||
| c6b2d2e1d9 | |||
| 1cf6d1dd9d | |||
| 8b8d147480 | |||
| 8fe21b8ef9 | |||
| 7c9f5d05db | |||
| 110b809b7d | |||
| 5e625c8d2e | |||
| 3f648f54c5 | |||
| d61074268a | |||
| cfb3a45479 | |||
| 7d7d7bcced | |||
| 6dfd330fa5 | |||
| 7f9349b7ae | |||
| 4c8f1910c8 | |||
| 3b364c2a3d | |||
| 0930407d0e | |||
| 4bb12c4ba4 | |||
| 8d6d99731f | |||
| b5902f4fbf | |||
| a455317122 | |||
| a8b400803b | |||
| fa85657801 | |||
| 424daede2f | |||
| e92c9c5619 | |||
| 7660dbfd77 | |||
| 15a7f43c7b | |||
| 1721e42988 | |||
| eabb846d07 | |||
| 280b0de646 | |||
| 2738b3e50f | |||
| 169795d673 | |||
| 085565a771 | |||
| 806519f78a | |||
| c3c5eaf914 | |||
| a9b94241af | |||
| 7066dc4ba7 |
+8
-13
@@ -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[]
|
||||
}
|
||||
|
||||
@@ -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" ]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
+5
-1
@@ -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:
|
||||
|
||||
@@ -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
@@ -1,4 +1,4 @@
|
||||
version=6.0.9
|
||||
version=6.0.10
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -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
-6
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
@@ -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) {
|
||||
|
||||
+7
-1
@@ -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;
|
||||
|
||||
+8
-2
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-14
@@ -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
|
||||
|
||||
+4
-9
@@ -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
|
||||
|
||||
+4
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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
-6
@@ -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;
|
||||
|
||||
+4
-8
@@ -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
|
||||
|
||||
+6
-16
@@ -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
|
||||
|
||||
+7
-12
@@ -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
|
||||
|
||||
+12
-8
@@ -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();
|
||||
}
|
||||
|
||||
+2
-1
@@ -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;
|
||||
|
||||
+3
-4
@@ -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)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+7
-2
@@ -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
|
||||
|
||||
+7
-6
@@ -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]);
|
||||
|
||||
+29
-4
@@ -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);
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
|
||||
+3
-3
@@ -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;
|
||||
}
|
||||
|
||||
+9
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+21
@@ -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);
|
||||
|
||||
+28
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+44
-11
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-1
@@ -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 {
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
+83
-133
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
-9
@@ -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
|
||||
|
||||
+7
-13
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSourcePointcut.java
Vendored
+16
-22
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-8
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -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());
|
||||
|
||||
+13
-1
@@ -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 {
|
||||
|
||||
+7
-1
@@ -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
|
||||
|
||||
+1
-3
@@ -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();
|
||||
|
||||
+10
-1
@@ -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 — 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 — 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 — 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 "";
|
||||
|
||||
|
||||
+5
-5
@@ -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);
|
||||
|
||||
+5
-3
@@ -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
|
||||
|
||||
+4
-4
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-5
@@ -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);
|
||||
|
||||
+1
-1
@@ -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());
|
||||
|
||||
+11
-25
@@ -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() {
|
||||
|
||||
+4
-3
@@ -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
|
||||
|
||||
+12
-9
@@ -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) {
|
||||
|
||||
+5
-2
@@ -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
|
||||
|
||||
+109
@@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
+16
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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 {
|
||||
|
||||
+6
-2
@@ -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
|
||||
|
||||
+2
-1
@@ -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;
|
||||
|
||||
+6
-2
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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"),
|
||||
|
||||
+7
-3
@@ -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;
|
||||
|
||||
|
||||
+1
-1
@@ -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
Reference in New Issue
Block a user