Compare commits

..

61 Commits

Author SHA1 Message Date
Spring Builds 91cf5ebd2d Release v6.0.19 2024-04-11 07:44:39 +00:00
Brian Clozel 402246df28 Refine UriComponentsBuilder parsing
This commit refines the expressions for the scheme, user info, host and
port parts of the URL in UriComponentsBuilder to better conform to
RFC 3986.

Fixes gh-32617
2024-04-11 08:50:23 +02:00
Stéphane Nicoll 88a68ddbd2 Upgrade to Reactor 2022.0.18
Closes gh-32593
2024-04-09 19:54:22 +02:00
Sam Brannen 7d178c6771 Detect bridge methods across ApplicationContexts in MethodIntrospector
Prior to this commit, MethodIntrospector failed to properly detect
bridge methods for subsequent invocations of selectMethods() with the
same targetType and MetadataLookup, if such subsequent invocations
occurred after the ApplicationContext had been refreshed.

The reason this occurs is due to the following.

- Class#getDeclaredMethods() always returns "child copies" of the
  underlying Method instances -- which means that `equals()` should be
  used instead of `==` whenever the compared Method instances can come
  from different sources (such as the static caches mentioned below).

- BridgeMethodResolver caches resolved bridge methods in a static cache
  -- which is never cleared.

- ReflectionUtils caches declared methods in a static cache
  -- which gets cleared when an ApplicationContext is refreshed.

Consequently, if you attempt to load an ApplicationContext twice in the
same ClassLoader, the second attempt uses the existing, populated cache
for bridged methods but a cleared, empty cache for declared methods.
This results in new invocations of Class#getDeclaredMethods(), and
identity checks with `==` then fail to detect equivalent bridge methods.

This commit addresses this by additionally comparing bridge methods
using `equals()` in MethodIntrospector.selectMethods().

Note that the `==` checks remain in place as an optimization for when
`equals()` is unnecessary.

Closes gh-32586

(cherry picked from commit e702733c7b)
2024-04-09 19:05:53 +02:00
Juergen Hoeller ba776d7201 Log column type for limited support message in getResultSetValue
Closes gh-32601

(cherry picked from commit c5590ae9e6)
2024-04-09 16:20:51 +02:00
Juergen Hoeller aba5f421fc Remove accidental backport of 6.1 class 2024-04-08 23:51:13 +02:00
Juergen Hoeller 2bac1629e6 Upgrade to Groovy 4.0.20, Netty 4.1.108, OpenPDF 1.3.43 2024-04-08 23:26:19 +02:00
Juergen Hoeller 9412d782ce Revised tests for generic FactoryBean type matching (backported)
See gh-32489
2024-04-08 23:00:33 +02:00
Juergen Hoeller a0bd13ceb1 Do not extract FactoryBean generic in case of targetType mismatch
Closes gh-32489
2024-04-08 10:16:48 +02:00
Stéphane Nicoll 46a0821205 Stop linking to Websphere's Javadoc
Closes gh-32582
2024-04-05 15:58:42 +02:00
Stéphane Nicoll 2c531344c6 Upgrade to Gradle 8.7
Closes gh-32567
2024-04-02 18:02:34 +02:00
Stéphane Nicoll 6610ee360e Upgrade actions that use deprecated features 2024-03-20 10:35:46 +01:00
Minsung Oh afde96c540 Fix link to vavr in the reference guide
Closes gh-32495
2024-03-20 10:03:29 +01:00
Sam Brannen da799bc519 Wrap InvalidMimeTypeException in HttpMediaTypeNotAcceptableException
The fix for #31254 resulted in an InvalidMimeTypeException being thrown
by MimeTypeUtils.sortBySpecificity() instead of an
IllegalArgumentException. However, InvalidMimeTypeException extends
IllegalArgumentException. Consequently, the change from
IllegalArgumentException to InvalidMimeTypeException did not result in
the desired effect in HeaderContentNegotiationStrategy.

HeaderContentNegotiationStrategy.resolveMediaTypes() still allows the
InvalidMimeTypeException to propagate as-is without wrapping it in an
HttpMediaTypeNotAcceptableException.

To address this issue, this commit catches InvalidMediaTypeException
and InvalidMimeTypeException in HeaderContentNegotiationStrategy and
wraps the exception in an HttpMediaTypeNotAcceptableException.

See gh-31254
See gh-31769
Closes gh-32483

(cherry picked from commit ef02f0bad8)
2024-03-19 15:55:15 +01:00
Sam Brannen 755968fd2c Polishing
(cherry picked from commit 836a0b3a40)
2024-03-19 15:55:08 +01:00
Juergen Hoeller eea00628f9 Polishing 2024-03-18 16:22:03 +01:00
Juergen Hoeller 8d745462b4 Propagate JMS IllegalStateException from commit/rollbackIfNecessary
Closes gh-32473

(cherry picked from commit cd7ba1835c)
2024-03-18 16:13:55 +01:00
Sam Brannen ed6c25fb6e Avoid unnecessary Annotation array cloning in TypeDescriptor
Closes gh-32476

(cherry picked from commit 42a4f28962)
2024-03-18 15:18:43 +01:00
Juergen Hoeller 8460a2d285 Restore original toString representation (revert accidental backport)
See gh-32405
2024-03-17 20:43:07 +01:00
Juergen Hoeller c3d186b4d0 Remove superfluous @NonNull declarations 2024-03-16 14:23:12 +01:00
Juergen Hoeller f8926d6a66 Avoid cloning empty Annotation array in TypeDescriptor (backport)
Closes gh-32405
2024-03-16 14:23:06 +01:00
Juergen Hoeller 19b21b15c1 Polishing 2024-03-15 21:22:49 +01:00
Juergen Hoeller 547425275c Consistently apply TaskDecorator to ManagedExecutorService as well
Closes gh-32455
2024-03-15 21:17:46 +01:00
Stéphane Nicoll 658194c155 Harmonize Concourse configuration 2024-03-15 09:15:24 +01:00
Stéphane Nicoll 769f73ebf1 Polish "Move CI to GitHub Actions"
See gh-32447
2024-03-15 09:14:22 +01:00
Stéphane Nicoll 80809bbc14 Move CI to GitHub Actions
Closes gh-32447
2024-03-15 09:00:28 +01:00
Spring Builds 6d323d710a Next development version (v6.0.19-SNAPSHOT) 2024-03-14 09:30:16 +00:00
rstoyanchev f2fd2f1226 Extract reusable checkSchemeAndPort method
Closes gh-32440
2024-03-14 08:48:42 +00:00
Juergen Hoeller 072ebb6ffc Additional unit tests for operations on empty UriTemplate
See gh-32432

(cherry picked from commit 54a6d89da7)
2024-03-13 18:18:52 +01:00
Kasper Bisgaard 08e7f7efa4 Allow UriTemplate to be built with an empty template
Closes gh-32437
2024-03-13 17:32:48 +01:00
Juergen Hoeller b1fafbf7e1 Upgrade to Reactor 2022.0.17
Includes Groovy 4.0.19, OpenPDF 1.3.42, Mockito 5.11

Closes gh-32421
2024-03-12 20:26:44 +01:00
Juergen Hoeller a3647a8c5e Polishing
(cherry picked from commit 723c94e5ac)
2024-03-12 20:26:24 +01:00
ZeroCyan 5c8d9cd0b2 Fix order of sections in Validation chapter of reference manual
Closes gh-32408
2024-03-10 17:31:30 +01:00
Juergen Hoeller 40d5196243 Polishing 2024-03-08 19:40:45 +01:00
Brian Clozel ec2c9b5d0e Set error on observation in WebClient instrumentation
Prior to this commit, error signals flowing from the client response
publisher in `WebClient` would be set on the `Observation.Context`. This
is enough for the observation convention to collect data about the error
but observation handlers are not notified of this error.

This commit sets the error instead on the observation directly to fix
this issue.

Fixes gh-32399
2024-03-08 11:36:20 +01:00
rstoyanchev 36539bdaa9 Use wrapped response in HandlerFunctionAdapter
webmvc.fn now also uses the StandardServletAsyncWebRequest wrapped response
to enforce lifecycle rules from Servlet spec (section 2.3.3.4).

See gh-32341
2024-03-07 14:51:01 +00:00
rstoyanchev 67ba7dd1da DisconnectedClientHelper recognizes AsyncRequestNotUsableException
See gh-32341
2024-03-06 18:20:31 +00:00
rstoyanchev 7b3fcf2647 Fix Javadoc error 2024-03-05 13:06:12 +00:00
rstoyanchev fd76c33589 Fix Javadoc error 2024-03-05 12:40:32 +00:00
rstoyanchev 1a7a6f421f Backport tests for wrapping of response for async requests
This is a backport of commits 4b96cd and ef0717.

Closes gh-32341
2024-03-05 12:03:48 +00:00
Juergen Hoeller 9ac1feceb5 Restore ability to return original method for proxy-derived method
Closes gh-32365
2024-03-04 23:32:27 +01:00
Juergen Hoeller 7029042e44 Polishing
(cherry picked from commit e9110c0729)
2024-03-04 23:31:51 +01:00
rstoyanchev 1a5661d426 Improve concurrent handling of result in WebAsyncManager
1. Use state transitions
2. Increase synchronized scope in setConcurrentResultAndDispatch

See gh-32341
2024-03-03 20:32:00 +00:00
rstoyanchev b208c63414 Add state and response wrapping to StandardServletAsyncWebRequest
The wrapped response prevents use after AsyncListener onError or completion
to ensure compliance with Servlet Spec 2.3.3.4.

The wrapped response is applied in RequestMappingHandlerAdapter.

The wrapped response raises AsyncRequestNotUsableException that is now
handled in DefaultHandlerExceptionResolver.

See gh-32341
2024-03-03 20:31:45 +00:00
rstoyanchev 814c003b43 Align 5.3.x with 6.1.x
In preparation for a larger update, start by aligning with
6.1.x, which includes changes for gh-32042 and gh-30232.

See gh-32341
2024-02-29 17:46:18 +00:00
Juergen Hoeller 5c34e1d11a Upgrade to Undertow 2.3.12, OpenPDF 1.3.41, JRuby 9.4.6 2024-02-28 21:38:57 +01:00
Juergen Hoeller d57775bbb2 Polishing 2024-02-28 21:38:38 +01:00
Juergen Hoeller b598ad3f33 Polishing 2024-02-28 19:26:11 +01:00
Juergen Hoeller b44ef70bc3 Direct reference to PushBuilder API on Servlet 5.0 baseline
See gh-29435
2024-02-28 19:17:13 +01:00
Sam Brannen d1b3107398 Do not cache Content-Type in ContentCachingResponseWrapper
Based on feedback from several members of the community, we have
decided to revert the caching of the Content-Type header that was
introduced in ContentCachingResponseWrapper in 375e0e6827.

This commit therefore completely removes Content-Type caching in
ContentCachingResponseWrapper and updates the existing tests
accordingly.

To provide guards against future regressions in this area, this commit
also introduces explicit tests for the 6 ways to set the content length
in ContentCachingResponseWrapper and modifies a test in
ShallowEtagHeaderFilterTests to ensure that a Content-Type header set
directly on ContentCachingResponseWrapper is propagated to the
underlying response even if content caching is disabled for the
ShallowEtagHeaderFilter.

See gh-32039
See gh-32317
Closes gh-32321
2024-02-28 10:51:48 +01:00
Sam Brannen 629c560316 Polish ShallowEtagHeaderFilterTests 2024-02-28 10:49:13 +01:00
Sébastien Deleuze 7bf07ef393 Refine *HttpMessageConverter#getContentLength null safety
Closes gh-32333
2024-02-27 15:48:36 +01:00
Sam Brannen ca602ef874 Honor Content-[Type|Length] headers from wrapped response again
Commit 375e0e6827 introduced a regression in
ContentCachingResponseWrapper (CCRW). Specifically, CCRW no longer
honors Content-Type and Content-Length headers that have been set in
the wrapped response and now incorrectly returns null for those header
values if they have not been set directly in the CCRW.

This commit fixes this regression as follows.

- The Content-Type and Content-Length headers set in the wrapped
  response are honored in getContentType(), containsHeader(),
  getHeader(), and getHeaders() unless those headers have been set
  directly in the CCRW.

- In copyBodyToResponse(), the Content-Type in the wrapped response is
  only overridden if the Content-Type has been set directly in the CCRW.

Furthermore, prior to this commit, getHeaderNames() returned duplicates
for the Content-Type and Content-Length headers if they were set in the
wrapped response as well as in CCRW.

This commit fixes that by returning a unique set from getHeaderNames().

This commit also updates ContentCachingResponseWrapperTests to verify
the expected behavior for Content-Type and Content-Length headers that
are set in the wrapped response as well as in CCRW.

See gh-32039
See gh-32317
Closes gh-32321
2024-02-25 17:35:54 +01:00
Sam Brannen e9bf5f5569 Polish ContentCachingResponseWrapper[Tests] 2024-02-25 17:35:54 +01:00
Juergen Hoeller 9a6f636e17 Consistent nullability for internal field access 2024-02-24 08:31:56 +01:00
Sébastien Deleuze 7686f5467e Adapt Hibernate native support for HHH-17643
This commit adapts Hibernate native support to handle
the changes performed as part of HHH-17643 which impacts
Hibernate versions 6.4.3+ and 6.2.21+.

It ignores the BytecodeProvider services loaded by the
service loader feature in order to default to the
"no-op" provider with native.

gh-32314 is expected to remove the need for such
substitutions which are not great for maintainability
by design.

Closes gh-32312
2024-02-22 17:06:53 +01:00
Juergen Hoeller 429c477f6a Polishing 2024-02-22 16:59:53 +01:00
Juergen Hoeller 5187281b50 Polishing 2024-02-21 22:56:21 +01:00
Juergen Hoeller 24a4487050 Add test for cleanup after configuration class creation failure
See gh-23343
2024-02-21 22:47:23 +01:00
Juergen Hoeller 8fba4a448a Polishing 2024-02-16 22:41:50 +01:00
Spring Builds 8f286de773 Next development version (v6.0.18-SNAPSHOT) 2024-02-15 12:34:07 +00:00
129 changed files with 3031 additions and 1423 deletions
@@ -0,0 +1,33 @@
name: Send notification
description: Sends a Google Chat message as a notification of the job's outcome
inputs:
webhook-url:
description: 'Google Chat Webhook URL'
required: true
status:
description: 'Status of the job'
required: true
build-scan-url:
description: 'URL of the build scan to include in the notification'
run-name:
description: 'Name of the run to include in the notification'
default: ${{ format('{0} {1}', github.ref_name, github.job) }}
runs:
using: composite
steps:
- shell: bash
run: |
echo "BUILD_SCAN=${{ inputs.build-scan-url == '' && ' [build scan unavailable]' || format(' [<{0}|Build Scan>]', inputs.build-scan-url) }}" >> "$GITHUB_ENV"
echo "RUN_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$GITHUB_ENV"
- shell: bash
if: ${{ inputs.status == 'success' }}
run: |
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was successful ${{ env.BUILD_SCAN }}"}' || true
- shell: bash
if: ${{ inputs.status == 'failure' }}
run: |
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<users/all> *<${{ env.RUN_URL }}|${{ inputs.run-name }}> failed* ${{ env.BUILD_SCAN }}"}' || true
- shell: bash
if: ${{ inputs.status == 'cancelled' }}
run: |
curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true
+6 -4
View File
@@ -18,11 +18,13 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
- name: Check out code
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
distribution: 'liberica'
java-version: 17
- name: Download BackportBot
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
- name: Backport
@@ -0,0 +1,64 @@
name: Build and deploy snapshot
on:
push:
branches:
- 6.0.x
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
build-and-deploy-snapshot:
if: ${{ github.repository == 'spring-projects/spring-framework' }}
name: Build and deploy snapshot
runs-on: ubuntu-latest
steps:
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'liberica'
java-version: 17
- name: Check out code
uses: actions/checkout@v4
- name: Set up Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
with:
cache-read-only: false
- name: Configure Gradle properties
shell: bash
run: |
mkdir -p $HOME/.gradle
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
- name: Build and publish
id: build
env:
CI: 'true'
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
- name: Deploy
uses: spring-io/artifactory-deploy-action@v0.0.1
with:
uri: 'https://repo.spring.io'
username: ${{ secrets.ARTIFACTORY_USERNAME }}
password: ${{ secrets.ARTIFACTORY_PASSWORD }}
build-name: ${{ format('spring-framework-{0}', github.ref_name)}}
repository: 'libs-snapshot-local'
folder: 'deployment-repository'
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
artifact-properties: |
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
/**/framework-api-*-docs.zip::zip.type=docs
/**/framework-api-*-schema.zip::zip.type=schema
- name: Send notification
uses: ./.github/actions/send-notification
if: always()
with:
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
status: ${{ job.status }}
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }}
+80
View File
@@ -0,0 +1,80 @@
name: CI
on:
push:
branches:
- 6.0.x
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
ci:
if: ${{ github.repository == 'spring-projects/spring-framework' }}
strategy:
matrix:
os:
- id: ubuntu-latest
name: Linux
java:
- version: 17
toolchain: false
- version: 21
toolchain: true
exclude:
- os:
name: Linux
java:
version: 17
name: '${{ matrix.os.name}} | Java ${{ matrix.java.version}}'
runs-on: ${{ matrix.os.id }}
steps:
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'liberica'
java-version: |
${{ matrix.java.version }}
${{ matrix.java.toolchain && '17' || '' }}
- name: Prepare Windows runner
if: ${{ runner.os == 'Windows' }}
run: |
git config --global core.autocrlf true
git config --global core.longPaths true
Stop-Service -name Docker
- name: Check out code
uses: actions/checkout@v4
- name: Set up Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
with:
cache-read-only: false
- name: Configure Gradle properties
shell: bash
run: |
mkdir -p $HOME/.gradle
echo 'systemProp.user.name=spring-builds+github' >> $HOME/.gradle/gradle.properties
echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties
echo 'org.gradle.daemon=4' >> $HOME/.gradle/gradle.properties
- name: Configure toolchain properties
if: ${{ matrix.java.toolchain }}
shell: bash
run: |
echo toolchainVersion=${{ matrix.java.version }} >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.auto-detect=false >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.auto-download=false >> $HOME/.gradle/gradle.properties
echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', matrix.java.version) }} >> $HOME/.gradle/gradle.properties
- name: Build
id: build
env:
CI: 'true'
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
run: ./gradlew check antora
- name: Send notification
uses: ./.github/actions/send-notification
if: always()
with:
webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }}
status: ${{ job.status }}
build-scan-url: ${{ steps.build.outputs.build-scan-url }}
run-name: ${{ format('{0} | {1} | Java {2}', github.ref_name, matrix.os.name, matrix.java.version) }}
+8 -6
View File
@@ -1,12 +1,14 @@
name: Deploy Docs
on:
push:
branches-ignore: [ gh-pages ]
tags: '**'
branches:
- 'main'
- '*.x'
- '!gh-pages'
tags:
- 'v*'
repository_dispatch:
types: request-build-reference # legacy
schedule:
- cron: '0 10 * * *' # Once per day at 10am UTC
workflow_dispatch:
permissions:
actions: write
@@ -15,8 +17,8 @@ jobs:
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects'
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Check out code
uses: actions/checkout@v4
with:
ref: docs-build
fetch-depth: 1
@@ -9,5 +9,5 @@ jobs:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v2
+1 -1
View File
@@ -1,4 +1,4 @@
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-6.0.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x?groups=Build") [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
# <img src="framework-docs/src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml/badge.svg?branch=6.0.x)](https://github.com/spring-projects/spring-framework/actions/workflows/build-and-deploy-snapshot.yml?query=branch%3A6.0.x) [![Revved up by Develocity](https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
-1
View File
@@ -113,7 +113,6 @@ configure([rootProject] + javaProjects) { project ->
"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 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://eclipse.dev/aspectj/doc/released/aspectj5rt-api",
+2
View File
@@ -1,5 +1,7 @@
== Spring Framework Concourse pipeline
NOTE: CI is being migrated to GitHub Actions.
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-6.0.x[Spring Framework 6.0.x].
+1 -1
View File
@@ -1,4 +1,4 @@
FROM ubuntu:jammy-20240111
FROM ubuntu:jammy-20240125
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
-1
View File
@@ -8,4 +8,3 @@ milestone: "6.0.x"
build-name: "spring-framework"
pipeline-name: "spring-framework"
concourse-url: "https://ci.spring.io"
task-timeout: 1h00m
+14 -77
View File
@@ -23,14 +23,6 @@ anchors:
docker-resource-source: &docker-resource-source
username: ((docker-hub-username))
password: ((docker-hub-password))
slack-fail-params: &slack-fail-params
text: >
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
[$TEXT_FILE_CONTENT]
text_file: git-repo/build/build-scan-uri.txt
silent: true
icon_emoji: ":concourse:"
username: concourse-ci
changelog-task-params: &changelog-task-params
name: generated-changelog/tag
tag: generated-changelog/tag
@@ -45,7 +37,7 @@ resource_types:
source:
<<: *docker-resource-source
repository: concourse/registry-image-resource
tag: 1.5.0
tag: 1.8.0
- name: artifactory-resource
type: registry-image
source:
@@ -57,19 +49,13 @@ resource_types:
source:
<<: *docker-resource-source
repository: concourse/github-release-resource
tag: 1.5.5
tag: 1.8.0
- name: github-status-resource
type: registry-image
source:
<<: *docker-resource-source
repository: dpb587/github-status-resource
tag: master
- name: slack-notification
type: registry-image
source:
<<: *docker-resource-source
repository: cfcommunity/slack-notification-resource
tag: latest
resources:
- name: git-repo
type: git
@@ -98,19 +84,6 @@ resources:
username: ((artifactory-username))
password: ((artifactory-password))
build_name: ((build-name))
- name: repo-status-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: build
- name: slack-alert
type: slack-notification
icon: slack
source:
url: ((slack-webhook-url))
- name: github-pre-release
type: github-release
icon: briefcase-download-outline
@@ -145,37 +118,23 @@ jobs:
- put: ci-image
params:
image: ci-image/image.tar
- name: build
- name: stage-milestone
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
trigger: true
- put: repo-status-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: build-project
image: ci-image
file: git-repo/ci/tasks/build-project.yml
privileged: true
timeout: ((task-timeout))
params:
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-build
params: { state: "success", commit: "git-repo" }
trigger: false
- task: stage
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: M
<<: *gradle-enterprise-task-params
- put: artifactory-repo
params: &artifactory-params
signing_key: ((signing-key))
signing_passphrase: ((signing-passphrase))
repo: libs-snapshot-local
repo: libs-staging-local
folder: distribution-repository
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
@@ -200,27 +159,9 @@ jobs:
- "/**/framework-docs-*-schema.zip"
properties:
"zip.type": "schema"
get_params:
threads: 8
- name: stage-milestone
serial: true
plan:
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: M
<<: *gradle-enterprise-task-params
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
- put: git-repo
params:
repository: stage-git-repo
- name: promote-milestone
serial: true
plan:
@@ -262,7 +203,6 @@ jobs:
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
@@ -307,7 +247,6 @@ jobs:
- put: artifactory-repo
params:
<<: *artifactory-params
repo: libs-staging-local
- put: git-repo
params:
repository: stage-git-repo
@@ -352,8 +291,6 @@ jobs:
<<: *changelog-task-params
groups:
- name: "builds"
jobs: ["build"]
- name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
-8
View File
@@ -1,8 +0,0 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 check
popd > /dev/null
-9
View File
@@ -1,9 +0,0 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
repository=$(pwd)/distribution-repository
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
popd > /dev/null
-9
View File
@@ -1,9 +0,0 @@
#!/bin/bash
set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check antora
popd > /dev/null
-19
View File
@@ -1,19 +0,0 @@
---
platform: linux
inputs:
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/build-pr.sh
-22
View File
@@ -1,22 +0,0 @@
---
platform: linux
inputs:
- name: git-repo
outputs:
- name: distribution-repository
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/build-project.sh
-24
View File
@@ -1,24 +0,0 @@
---
platform: linux
inputs:
- name: git-repo
outputs:
- name: distribution-repository
- name: git-repo
caches:
- path: gradle
params:
BRANCH:
CI: true
MAIN_TOOLCHAIN:
TEST_TOOLCHAIN:
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
GRADLE_ENTERPRISE_URL: https://ge.spring.io
run:
path: bash
args:
- -ec
- |
${PWD}/git-repo/ci/scripts/check-project.sh
+1 -1
View File
@@ -4,7 +4,7 @@ image_resource:
type: registry-image
source:
repository: springio/concourse-release-scripts
tag: '0.4.0-SNAPSHOT'
tag: '0.4.0'
username: ((docker-hub-username))
password: ((docker-hub-password))
inputs:
+2 -2
View File
@@ -39,8 +39,8 @@
** xref:core/resources.adoc[]
** xref:core/validation.adoc[]
*** xref:core/validation/validator.adoc[]
*** xref:core/validation/conversion.adoc[]
*** xref:core/validation/beans-beans.adoc[]
*** xref:core/validation/conversion.adoc[]
*** xref:core/validation/convert.adoc[]
*** xref:core/validation/format.adoc[]
*** xref:core/validation/format-configuring-formatting-globaldatetimeformat.adoc[]
@@ -431,4 +431,4 @@
** xref:languages/groovy.adoc[]
** xref:languages/dynamic.adoc[]
* xref:appendix.adoc[]
* https://github.com/spring-projects/spring-framework/wiki[Wiki]
* https://github.com/spring-projects/spring-framework/wiki[Wiki]
@@ -268,8 +268,8 @@ The actual JPA provider bootstrapping is handed off to the specified executor an
running in parallel, to the application bootstrap thread. The exposed `EntityManagerFactory`
proxy can be injected into other application components and is even able to respond to
`EntityManagerFactoryInfo` configuration inspection. However, once the actual JPA provider
is being accessed by other components (for example, calling `createEntityManager`), those calls
block until the background bootstrapping has completed. In particular, when you use
is being accessed by other components (for example, calling `createEntityManager`), those
calls block until the background bootstrapping has completed. In particular, when you use
Spring Data JPA, make sure to set up deferred bootstrapping for its repositories as well.
@@ -284,9 +284,9 @@ to a newly created `EntityManager` per operation, in effect making its usage thr
It is possible to write code against the plain JPA without any Spring dependencies, by
using an injected `EntityManagerFactory` or `EntityManager`. Spring can understand the
`@PersistenceUnit` and `@PersistenceContext` annotations both at the field and the method level
if a `PersistenceAnnotationBeanPostProcessor` is enabled. The following example shows a plain
JPA DAO implementation that uses the `@PersistenceUnit` annotation:
`@PersistenceUnit` and `@PersistenceContext` annotations both at the field and the method
level if a `PersistenceAnnotationBeanPostProcessor` is enabled. The following example
shows a plain JPA DAO implementation that uses the `@PersistenceUnit` annotation:
[tabs]
======
@@ -23,7 +23,7 @@ As of Spring Framework 5.2, the default configuration also provides support for
Vavr's `Try` method to trigger transaction rollbacks when it returns a 'Failure'.
This allows you to handle functional-style errors using Try and have the transaction
automatically rolled back in case of a failure. For more information on Vavr's Try,
refer to the [official Vavr documentation](https://www.vavr.io/vavr-docs/#_try).
refer to the https://docs.vavr.io/#_try[official Vavr documentation].
Here's an example of how to use Vavr's Try with a transactional method:
[tabs]
+9 -9
View File
@@ -9,23 +9,23 @@ javaPlatform {
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.14.3"))
api(platform("io.micrometer:micrometer-bom:1.10.13"))
api(platform("io.netty:netty-bom:4.1.107.Final"))
api(platform("io.netty:netty-bom:4.1.108.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2022.0.16"))
api(platform("io.projectreactor:reactor-bom:2022.0.18"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.18"))
api(platform("org.apache.groovy:groovy-bom:4.0.20"))
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
api(platform("org.eclipse.jetty:jetty-bom:11.0.20"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.4.0"))
api(platform("org.junit:junit-bom:5.9.3"))
api(platform("org.mockito:mockito-bom:5.10.0"))
api(platform("org.mockito:mockito-bom:5.11.0"))
constraints {
api("com.fasterxml:aalto-xml:1.3.2")
api("com.fasterxml.woodstox:woodstox-core:6.5.1")
api("com.github.ben-manes.caffeine:caffeine:3.1.8")
api("com.github.librepdf:openpdf:1.3.40")
api("com.github.librepdf:openpdf:1.3.43")
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")
@@ -54,9 +54,9 @@ dependencies {
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
api("io.reactivex.rxjava3:rxjava:3.1.8")
api("io.smallrye.reactive:mutiny:1.10.0")
api("io.undertow:undertow-core:2.3.11.Final")
api("io.undertow:undertow-servlet:2.3.11.Final")
api("io.undertow:undertow-websockets-jsr:2.3.11.Final")
api("io.undertow:undertow-core:2.3.12.Final")
api("io.undertow:undertow-servlet:2.3.12.Final")
api("io.undertow:undertow-websockets-jsr:2.3.12.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")
@@ -126,7 +126,7 @@ dependencies {
api("org.hibernate:hibernate-validator:7.0.5.Final")
api("org.hsqldb:hsqldb:2.7.2")
api("org.javamoney:moneta:1.4.2")
api("org.jruby:jruby:9.4.5.0")
api("org.jruby:jruby:9.4.6.0")
api("org.junit.support:testng-engine:1.0.4")
api("org.mozilla:rhino:1.7.14")
api("org.ogce:xpp3:1.1.6")
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.0.17
version=6.0.19
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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.
@@ -186,10 +186,10 @@ public abstract class AopUtils {
* this method resolves bridge methods in order to retrieve attributes from
* the <i>original</i> method definition.
* @param method the method to be invoked, which may come from an interface
* @param targetClass the target class for the current invocation.
* May be {@code null} or may not even implement the method.
* @param targetClass the target class for the current invocation
* (can be {@code null} or may not even implement the method)
* @return the specific target method, or the original method if the
* {@code targetClass} doesn't implement it or is {@code null}
* {@code targetClass} does not implement it
* @see org.springframework.util.ClassUtils#getMostSpecificMethod
*/
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,29 +17,35 @@
package org.springframework.aop.support;
import java.lang.reflect.Method;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.target.EmptyTargetSource;
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.core.ResolvableType;
import org.springframework.core.testfixture.io.SerializationTestUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rod Johnson
* @author Chris Beams
* @author Sebastien Deleuze
* @author Juergen Hoeller
*/
public class AopUtilsTests {
class AopUtilsTests {
@Test
public void testPointcutCanNeverApply() {
void testPointcutCanNeverApply() {
class TestPointcut extends StaticMethodMatcherPointcut {
@Override
public boolean matches(Method method, @Nullable Class<?> clazzy) {
@@ -52,13 +58,13 @@ public class AopUtilsTests {
}
@Test
public void testPointcutAlwaysApplies() {
void testPointcutAlwaysApplies() {
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), Object.class)).isTrue();
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), TestBean.class)).isTrue();
}
@Test
public void testPointcutAppliesToOneMethodOnObject() {
void testPointcutAppliesToOneMethodOnObject() {
class TestPointcut extends StaticMethodMatcherPointcut {
@Override
public boolean matches(Method method, @Nullable Class<?> clazz) {
@@ -78,7 +84,7 @@ public class AopUtilsTests {
* that's subverted the singleton construction limitation.
*/
@Test
public void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
assertThat(SerializationTestUtils.serializeAndDeserialize(MethodMatcher.TRUE)).isSameAs(MethodMatcher.TRUE);
assertThat(SerializationTestUtils.serializeAndDeserialize(ClassFilter.TRUE)).isSameAs(ClassFilter.TRUE);
assertThat(SerializationTestUtils.serializeAndDeserialize(Pointcut.TRUE)).isSameAs(Pointcut.TRUE);
@@ -88,4 +94,45 @@ public class AopUtilsTests {
assertThat(SerializationTestUtils.serializeAndDeserialize(ExposeInvocationInterceptor.INSTANCE)).isSameAs(ExposeInvocationInterceptor.INSTANCE);
}
@Test
void testInvokeJoinpointUsingReflection() throws Throwable {
String name = "foo";
TestBean testBean = new TestBean(name);
Method method = ReflectionUtils.findMethod(TestBean.class, "getName");
Object result = AopUtils.invokeJoinpointUsingReflection(testBean, method, new Object[0]);
assertThat(result).isEqualTo(name);
}
@Test // gh-32365
void mostSpecificMethodBetweenJdkProxyAndTarget() throws Exception {
Class<?> proxyClass = new ProxyFactory(new WithInterface()).getProxyClass(getClass().getClassLoader());
Method specificMethod = AopUtils.getMostSpecificMethod(proxyClass.getMethod("handle", List.class), WithInterface.class);
assertThat(ResolvableType.forMethodParameter(specificMethod, 0).getGeneric().toClass()).isEqualTo(String.class);
}
@Test // gh-32365
void mostSpecificMethodBetweenCglibProxyAndTarget() throws Exception {
Class<?> proxyClass = new ProxyFactory(new WithoutInterface()).getProxyClass(getClass().getClassLoader());
Method specificMethod = AopUtils.getMostSpecificMethod(proxyClass.getMethod("handle", List.class), WithoutInterface.class);
assertThat(ResolvableType.forMethodParameter(specificMethod, 0).getGeneric().toClass()).isEqualTo(String.class);
}
interface ProxyInterface {
void handle(List<String> list);
}
static class WithInterface implements ProxyInterface {
public void handle(List<String> list) {
}
}
static class WithoutInterface {
public void handle(List<String> list) {
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -161,6 +161,9 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
/** Map from scope identifier String to corresponding Scope. */
private final Map<String, Scope> scopes = new LinkedHashMap<>(8);
/** Application startup metrics. **/
private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
/** Map from bean name to merged RootBeanDefinition. */
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
@@ -171,8 +174,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
new NamedThreadLocal<>("Prototype beans currently in creation");
/** Application startup metrics. **/
private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
/**
* Create a new AbstractBeanFactory.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -609,13 +609,10 @@ class ConstructorResolver {
String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"No matching factory method found on class [" + factoryClass.getName() + "]: " +
(mbd.getFactoryBeanName() != null ?
"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
(mbd.getFactoryBeanName() != null ? "factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +
"Check that a method with the specified name " +
(minNrOfArgs > 0 ? "and arguments " : "") +
"exists and that it is " +
(isStatic ? "static" : "non-static") + ".");
"Check that a method with the specified name " + (minNrOfArgs > 0 ? "and arguments " : "") +
"exists and that it is " + (isStatic ? "static" : "non-static") + ".");
}
else if (void.class == factoryMethodToUse.getReturnType()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -567,16 +567,16 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
*/
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
// Trigger destruction of dependent beans first...
Set<String> dependencies;
Set<String> dependentBeanNames;
synchronized (this.dependentBeanMap) {
// Within full synchronization in order to guarantee a disconnected Set
dependencies = this.dependentBeanMap.remove(beanName);
dependentBeanNames = this.dependentBeanMap.remove(beanName);
}
if (dependencies != null) {
if (dependentBeanNames != null) {
if (logger.isTraceEnabled()) {
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependentBeanNames);
}
for (String dependentBeanName : dependencies) {
for (String dependentBeanName : dependentBeanNames) {
destroySingleton(dependentBeanName);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -108,7 +108,8 @@ public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCan
Class<?> resolvedClass = targetType.resolve();
if (resolvedClass != null && FactoryBean.class.isAssignableFrom(resolvedClass)) {
Class<?> typeToBeMatched = dependencyType.resolve();
if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched)) {
if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched) &&
!typeToBeMatched.isAssignableFrom(resolvedClass)) {
targetType = targetType.getGeneric();
if (descriptor.fallbackMatchAllowed()) {
// Matching the Class-based type determination for FactoryBean
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 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,12 +29,10 @@ import org.springframework.core.MethodClassKey;
import org.springframework.lang.Nullable;
/**
* Abstract implementation of {@link JCacheOperationSource} that caches attributes
* Abstract implementation of {@link JCacheOperationSource} that caches operations
* for methods and implements a fallback policy: 1. specific target method;
* 2. declaring method.
*
* <p>This implementation caches attributes by method after they are first used.
*
* @author Stephane Nicoll
* @author Juergen Hoeller
* @since 4.1
@@ -43,24 +41,25 @@ import org.springframework.lang.Nullable;
public abstract class AbstractFallbackJCacheOperationSource implements JCacheOperationSource {
/**
* Canonical value held in cache to indicate no caching attribute was
* found for this method and we don't need to look again.
* Canonical value held in cache to indicate no cache operation was
* found for this method, and we don't need to look again.
*/
private static final Object NULL_CACHING_ATTRIBUTE = new Object();
private static final Object NULL_CACHING_MARKER = new Object();
protected final Log logger = LogFactory.getLog(getClass());
private final Map<MethodClassKey, Object> cache = new ConcurrentHashMap<>(1024);
private final Map<MethodClassKey, Object> operationCache = new ConcurrentHashMap<>(1024);
@Override
@Nullable
public JCacheOperation<?> getCacheOperation(Method method, @Nullable Class<?> targetClass) {
MethodClassKey cacheKey = new MethodClassKey(method, targetClass);
Object cached = this.cache.get(cacheKey);
Object cached = this.operationCache.get(cacheKey);
if (cached != null) {
return (cached != NULL_CACHING_ATTRIBUTE ? (JCacheOperation<?>) cached : null);
return (cached != NULL_CACHING_MARKER ? (JCacheOperation<?>) cached : null);
}
else {
JCacheOperation<?> operation = computeCacheOperation(method, targetClass);
@@ -68,10 +67,10 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
if (logger.isDebugEnabled()) {
logger.debug("Adding cacheable method '" + method.getName() + "' with operation: " + operation);
}
this.cache.put(cacheKey, operation);
this.operationCache.put(cacheKey, operation);
}
else {
this.cache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
this.operationCache.put(cacheKey, NULL_CACHING_MARKER);
}
return operation;
}
@@ -84,7 +83,7 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
return null;
}
// The method may be on an interface, but we need attributes from the target class.
// The method may be on an interface, but we need metadata from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2024 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.
@@ -212,10 +212,8 @@ public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJC
for (Class<?> parameterType : parameterTypes) {
parameters.add(parameterType.getName());
}
return method.getDeclaringClass().getName()
+ '.' + method.getName()
+ '(' + StringUtils.collectionToCommaDelimitedString(parameters) + ')';
return method.getDeclaringClass().getName() + '.' + method.getName() +
'(' + StringUtils.collectionToCommaDelimitedString(parameters) + ')';
}
private int countNonNull(Object... instances) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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 class BeanFactoryJCacheOperationSourceAdvisor extends AbstractBeanFactory
* 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 JCacheInterceptor#setCacheOperationSource
*/
public void setCacheOperationSource(JCacheOperationSource cacheOperationSource) {
this.pointcut.setCacheOperationSource(cacheOperationSource);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ public interface JCacheOperationSource {
* Return the cache operations for this method, or {@code null}
* if the method contains no <em>JSR-107</em> related metadata.
* @param method the method to introspect
* @param targetClass the target class (may be {@code null}, in which case
* @param targetClass the target class (can be {@code null}, in which case
* the declaring class of the method must be used)
* @return the cache operation for this method, or {@code null} if none found
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* A Pointcut that matches if the underlying {@link JCacheOperationSource}
* A {@code Pointcut} that matches if the underlying {@link JCacheOperationSource}
* has an operation for a given method.
*
* @author Stephane Nicoll
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 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.
@@ -32,20 +32,16 @@ import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* Abstract implementation of {@link CacheOperation} that caches attributes
* Abstract implementation of {@link CacheOperationSource} that caches operations
* for methods and implements a fallback policy: 1. specific target method;
* 2. target class; 3. declaring method; 4. declaring class/interface.
*
* <p>Defaults to using the target class's caching attribute if none is
* associated with the target method. Any caching attribute associated with
* the target method completely overrides a class caching attribute.
* <p>Defaults to using the target class's declared cache operations if none are
* associated with the target method. Any cache operations associated with
* the target method completely override any class-level declarations.
* If none found on the target class, the interface that the invoked method
* has been called through (in case of a JDK proxy) will be checked.
*
* <p>This implementation caches attributes by method after they are first
* used. If it is ever desirable to allow dynamic changing of cacheable
* attributes (which is very unlikely), caching could be made configurable.
*
* @author Costin Leau
* @author Juergen Hoeller
* @since 3.1
@@ -53,10 +49,10 @@ import org.springframework.util.ClassUtils;
public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource {
/**
* Canonical value held in cache to indicate no caching attribute was
* found for this method and we don't need to look again.
* Canonical value held in cache to indicate no cache operation was
* found for this method, and we don't need to look again.
*/
private static final Collection<CacheOperation> NULL_CACHING_ATTRIBUTE = Collections.emptyList();
private static final Collection<CacheOperation> NULL_CACHING_MARKER = Collections.emptyList();
/**
@@ -71,14 +67,14 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
* <p>As this base class is not marked Serializable, the cache will be recreated
* after serialization - provided that the concrete subclass is Serializable.
*/
private final Map<Object, Collection<CacheOperation>> attributeCache = new ConcurrentHashMap<>(1024);
private final Map<Object, Collection<CacheOperation>> operationCache = new ConcurrentHashMap<>(1024);
/**
* Determine the caching attribute for this method invocation.
* <p>Defaults to the class's caching attribute if no method attribute is found.
* Determine the cache operations for this method invocation.
* <p>Defaults to class-declared metadata if no method-level metadata is found.
* @param method the method for the current invocation (never {@code null})
* @param targetClass the target class for this invocation (may be {@code null})
* @param targetClass the target class for this invocation (can be {@code null})
* @return {@link CacheOperation} for this method, or {@code null} if the method
* is not cacheable
*/
@@ -90,21 +86,21 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
}
Object cacheKey = getCacheKey(method, targetClass);
Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
Collection<CacheOperation> cached = this.operationCache.get(cacheKey);
if (cached != null) {
return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
return (cached != NULL_CACHING_MARKER ? cached : null);
}
else {
Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
if (cacheOps != null) {
if (logger.isTraceEnabled()) {
logger.trace("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
logger.trace("Adding cacheable method '" + method.getName() + "' with operations: " + cacheOps);
}
this.attributeCache.put(cacheKey, cacheOps);
this.operationCache.put(cacheKey, cacheOps);
}
else {
this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
this.operationCache.put(cacheKey, NULL_CACHING_MARKER);
}
return cacheOps;
}
@@ -129,7 +125,7 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
return null;
}
// The method may be on an interface, but we need attributes from the target class.
// The method may be on an interface, but we need metadata from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
@@ -163,19 +159,19 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
/**
* Subclasses need to implement this to return the caching attribute for the
* Subclasses need to implement this to return the cache operations for the
* given class, if any.
* @param clazz the class to retrieve the attribute for
* @return all caching attribute associated with this class, or {@code null} if none
* @param clazz the class to retrieve the cache operations for
* @return all cache operations associated with this class, or {@code null} if none
*/
@Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz);
/**
* Subclasses need to implement this to return the caching attribute for the
* Subclasses need to implement this to return the cache operations for the
* given method, if any.
* @param method the method to retrieve the attribute for
* @return all caching attribute associated with this method, or {@code null} if none
* @param method the method to retrieve the cache operations for
* @return all cache operations associated with this method, or {@code null} if none
*/
@Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Method method);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,7 +54,7 @@ public interface CacheOperationSource {
* Return the collection of cache operations for this method,
* or {@code null} if the method contains no <em>cacheable</em> annotations.
* @param method the method to introspect
* @param targetClass the target class (may be {@code null}, in which case
* @param targetClass the target class (can be {@code null}, in which case
* the declaring class of the method must be used)
* @return all cache operations for this method, or {@code null} if none found
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -28,7 +28,7 @@ import org.springframework.util.ObjectUtils;
/**
* A {@code Pointcut} that matches if the underlying {@link CacheOperationSource}
* has an attribute for a given method.
* has an operation for a given method.
*
* @author Costin Leau
* @author Juergen Hoeller
@@ -36,7 +36,7 @@ import org.springframework.util.ObjectUtils;
* @since 3.1
*/
@SuppressWarnings("serial")
class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
final class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
@Nullable
private CacheOperationSource cacheOperationSource;
@@ -78,7 +78,7 @@ class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implement
* {@link ClassFilter} that delegates to {@link CacheOperationSource#isCandidateClass}
* for filtering classes whose methods are not worth searching to begin with.
*/
private class CacheOperationSourceClassFilter implements ClassFilter {
private final class CacheOperationSourceClassFilter implements ClassFilter {
@Override
public boolean matches(Class<?> clazz) {
@@ -88,6 +88,7 @@ class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implement
return (cacheOperationSource == null || cacheOperationSource.isCandidateClass(clazz));
}
@Nullable
private CacheOperationSource getCacheOperationSource() {
return cacheOperationSource;
}
@@ -95,7 +96,7 @@ class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implement
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof CacheOperationSourceClassFilter that &&
ObjectUtils.nullSafeEquals(cacheOperationSource, that.getCacheOperationSource())));
ObjectUtils.nullSafeEquals(getCacheOperationSource(), that.getCacheOperationSource())));
}
@Override
@@ -105,9 +106,8 @@ class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implement
@Override
public String toString() {
return CacheOperationSourceClassFilter.class.getName() + ": " + cacheOperationSource;
return CacheOperationSourceClassFilter.class.getName() + ": " + getCacheOperationSource();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -73,7 +73,6 @@ final class ConfigurationClass {
* Create a new {@link ConfigurationClass} with the given name.
* @param metadataReader reader used to parse the underlying {@link Class}
* @param beanName must not be {@code null}
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
ConfigurationClass(MetadataReader metadataReader, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
@@ -87,10 +86,10 @@ final class ConfigurationClass {
* using the {@link Import} annotation or automatically processed as a nested
* configuration class (if importedBy is not {@code null}).
* @param metadataReader reader used to parse the underlying {@link Class}
* @param importedBy the configuration class importing this one or {@code null}
* @param importedBy the configuration class importing this one
* @since 3.1.1
*/
ConfigurationClass(MetadataReader metadataReader, @Nullable ConfigurationClass importedBy) {
ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) {
this.metadata = metadataReader.getAnnotationMetadata();
this.resource = metadataReader.getResource();
this.importedBy.add(importedBy);
@@ -100,7 +99,6 @@ final class ConfigurationClass {
* Create a new {@link ConfigurationClass} with the given name.
* @param clazz the underlying {@link Class} to represent
* @param beanName name of the {@code @Configuration} class bean
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
ConfigurationClass(Class<?> clazz, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
@@ -114,10 +112,10 @@ final class ConfigurationClass {
* using the {@link Import} annotation or automatically processed as a nested
* configuration class (if imported is {@code true}).
* @param clazz the underlying {@link Class} to represent
* @param importedBy the configuration class importing this one (or {@code null})
* @param importedBy the configuration class importing this one
* @since 3.1.1
*/
ConfigurationClass(Class<?> clazz, @Nullable ConfigurationClass importedBy) {
ConfigurationClass(Class<?> clazz, ConfigurationClass importedBy) {
this.metadata = AnnotationMetadata.introspect(clazz);
this.resource = new DescriptiveResource(clazz.getName());
this.importedBy.add(importedBy);
@@ -127,7 +125,6 @@ final class ConfigurationClass {
* Create a new {@link ConfigurationClass} with the given name.
* @param metadata the metadata for the underlying class to represent
* @param beanName name of the {@code @Configuration} class bean
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
ConfigurationClass(AnnotationMetadata metadata, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
@@ -149,12 +146,12 @@ final class ConfigurationClass {
return ClassUtils.getShortName(getMetadata().getClassName());
}
void setBeanName(String beanName) {
void setBeanName(@Nullable String beanName) {
this.beanName = beanName;
}
@Nullable
public String getBeanName() {
String getBeanName() {
return this.beanName;
}
@@ -164,7 +161,7 @@ final class ConfigurationClass {
* @since 3.1.1
* @see #getImportedBy()
*/
public boolean isImported() {
boolean isImported() {
return !this.importedBy.isEmpty();
}
@@ -198,6 +195,10 @@ final class ConfigurationClass {
this.importedResources.put(importedResource, readerClass);
}
Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
return this.importedResources;
}
void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
}
@@ -206,10 +207,6 @@ final class ConfigurationClass {
return this.importBeanDefinitionRegistrars;
}
Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
return this.importedResources;
}
void validate(ProblemReporter problemReporter) {
Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
@@ -135,17 +135,6 @@ import org.springframework.util.ReflectionUtils;
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
/**
* The name of the {@link LifecycleProcessor} bean in the context.
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
* @since 3.0
* @see org.springframework.context.LifecycleProcessor
* @see org.springframework.context.support.DefaultLifecycleProcessor
* @see #start()
* @see #stop()
*/
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
/**
* The name of the {@link MessageSource} bean in the context.
* If none is supplied, message resolution is delegated to the parent.
@@ -166,6 +155,17 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
*/
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
/**
* The name of the {@link LifecycleProcessor} bean in the context.
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
* @since 3.0
* @see org.springframework.context.LifecycleProcessor
* @see org.springframework.context.support.DefaultLifecycleProcessor
* @see #start()
* @see #stop()
*/
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
static {
// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
@@ -796,8 +796,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
/**
* Initialize the MessageSource.
* Use parent's if none defined in this context.
* Initialize the {@link MessageSource}.
* <p>Uses parent's {@code MessageSource} if none defined in this context.
* @see #MESSAGE_SOURCE_BEAN_NAME
*/
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
@@ -827,8 +828,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
/**
* Initialize the ApplicationEventMulticaster.
* Uses SimpleApplicationEventMulticaster if none defined in the context.
* Initialize the {@link ApplicationEventMulticaster}.
* <p>Uses {@link SimpleApplicationEventMulticaster} if none defined in the context.
* @see #APPLICATION_EVENT_MULTICASTER_BEAN_NAME
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
*/
protected void initApplicationEventMulticaster() {
@@ -851,15 +853,16 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
/**
* Initialize the LifecycleProcessor.
* Uses DefaultLifecycleProcessor if none defined in the context.
* Initialize the {@link LifecycleProcessor}.
* <p>Uses {@link DefaultLifecycleProcessor} if none defined in the context.
* @since 3.0
* @see #LIFECYCLE_PROCESSOR_BEAN_NAME
* @see org.springframework.context.support.DefaultLifecycleProcessor
*/
protected void initLifecycleProcessor() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
this.lifecycleProcessor =
beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
this.lifecycleProcessor = beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
if (logger.isTraceEnabled()) {
logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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.
@@ -98,8 +98,8 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd
* applying the corresponding default if a supplier is not resolvable.
* @since 5.1
*/
public void configure(
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
public void configure(@Nullable Supplier<Executor> executor,
@Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
this.executor = executor;
this.exceptionHandler = exceptionHandler;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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 ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
* execution callback (which may be a wrapper around the user-supplied task).
* <p>The primary use case is to set some execution context around the task's
* invocation, or to provide some monitoring/statistics for task execution.
* <p><b>NOTE:</b> Exception handling in {@code TaskDecorator} implementations
* is limited to plain {@code Runnable} execution via {@code execute} calls.
* In case of {@code #submit} calls, the exposed {@code Runnable} will be a
* {@code FutureTask} which does not propagate any exceptions; you might
* have to cast it and call {@code Future#get} to evaluate exceptions.
* @since 4.3
*/
public final void setTaskDecorator(TaskDecorator taskDecorator) {
@@ -179,11 +174,10 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
}
private TaskExecutorAdapter getAdaptedExecutor(Executor concurrentExecutor) {
if (managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(concurrentExecutor)) {
return new ManagedTaskExecutorAdapter(concurrentExecutor);
}
TaskExecutorAdapter adapter = new TaskExecutorAdapter(concurrentExecutor);
private TaskExecutorAdapter getAdaptedExecutor(Executor originalExecutor) {
TaskExecutorAdapter adapter =
(managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(originalExecutor) ?
new ManagedTaskExecutorAdapter(originalExecutor) : new TaskExecutorAdapter(originalExecutor));
if (this.taskDecorator != null) {
adapter.setTaskDecorator(this.taskDecorator);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -183,6 +183,7 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
* @see Clock#systemDefaultZone()
*/
public void setClock(Clock clock) {
Assert.notNull(clock, "Clock must not be null");
this.clock = clock;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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,8 +46,9 @@ import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureTask;
/**
* Implementation of Spring's {@link TaskScheduler} interface, wrapping
* a native {@link java.util.concurrent.ScheduledThreadPoolExecutor}.
* A standard implementation of Spring's {@link TaskScheduler} interface, wrapping
* a native {@link java.util.concurrent.ScheduledThreadPoolExecutor} and providing
* all applicable configuration options for it.
*
* @author Juergen Hoeller
* @author Mark Fisher
@@ -158,6 +159,7 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
* @see Clock#systemDefaultZone()
*/
public void setClock(Clock clock) {
Assert.notNull(clock, "Clock must not be null");
this.clock = clock;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -307,8 +307,8 @@ class AnnotationConfigApplicationContextTests {
assertThat(ObjectUtils.containsElement(context.getBeanNamesForType(BeanC.class), "c")).isTrue();
assertThat(context.getBeansOfType(BeanA.class)).isEmpty();
assertThat(context.getBeansOfType(BeanB.class).values().iterator().next()).isSameAs(context.getBean(BeanB.class));
assertThat(context.getBeansOfType(BeanC.class).values().iterator().next()).isSameAs(context.getBean(BeanC.class));
assertThat(context.getBeansOfType(BeanB.class).values()).singleElement().isSameAs(context.getBean(BeanB.class));
assertThat(context.getBeansOfType(BeanC.class).values()).singleElement().isSameAs(context.getBean(BeanC.class));
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
.isThrownBy(() -> context.getBeanFactory().resolveNamedBean(BeanA.class));
@@ -409,15 +409,17 @@ class AnnotationConfigApplicationContextTests {
bd2.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class)));
bd2.setLazyInit(true);
context.registerBeanDefinition("fb2", bd2);
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryBeanInjectionPoints.class));
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryBeanInjectionPoints.class);
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition("ip", bd3);
context.refresh();
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryBean).isSameAs(context.getBean("&fb1"));
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class);
assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class);
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
assertThat(context.getBeanNamesForType(GenericHolderFactoryBean.class)).hasSize(1);
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryBean).isSameAs(context.getBean("&fb1"));
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryResult).isSameAs(context.getBean("fb1"));
}
@Test
@@ -425,7 +427,7 @@ class AnnotationConfigApplicationContextTests {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
RootBeanDefinition bd1 = new RootBeanDefinition();
bd1.setBeanClass(GenericHolderFactoryBean.class);
bd1.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Object.class)));
bd1.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, String.class)));
bd1.setLazyInit(true);
context.registerBeanDefinition("fb1", bd1);
RootBeanDefinition bd2 = new RootBeanDefinition();
@@ -433,13 +435,17 @@ class AnnotationConfigApplicationContextTests {
bd2.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class)));
bd2.setLazyInit(true);
context.registerBeanDefinition("fb2", bd2);
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class));
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryBeanInjectionPoints.class);
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition("ip", bd3);
context.refresh();
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class);
assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class);
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
}
@Test
@@ -453,14 +459,17 @@ class AnnotationConfigApplicationContextTests {
bd2.setBeanClass(UntypedFactoryBean.class);
bd2.setTargetType(ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class));
context.registerBeanDefinition("fb2", bd2);
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class));
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryResultInjectionPoint.class);
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition("ip", bd3);
context.refresh();
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class);
assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class);
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
assertThat(context.getBeanNamesForType(GenericHolderFactoryBean.class)).hasSize(1);
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -268,6 +268,7 @@ public class ClassPathBeanDefinitionScannerTests {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
scanner.scan("org.springframework.context.annotation2");
assertThatIllegalStateException().isThrownBy(() -> scanner.scan(BASE_PACKAGE))
.withMessageContaining("myNamedDao")
.withMessageContaining(NamedStubDao.class.getName())
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,8 @@ import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
@@ -321,8 +323,8 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.setEnvironment(new StandardEnvironment());
pp.postProcessBeanFactory(beanFactory);
assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() ->
beanFactory.getBean(SimpleComponent.class));
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
.isThrownBy(() -> beanFactory.getBean(SimpleComponent.class));
}
@Test
@@ -372,11 +374,11 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class));
beanFactory.setAllowBeanDefinitionOverriding(false);
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() ->
pp.postProcessBeanFactory(beanFactory))
.withMessageContaining("bar")
.withMessageContaining("SingletonBeanConfig")
.withMessageContaining(TestBean.class.getName());
assertThatExceptionOfType(BeanDefinitionStoreException.class)
.isThrownBy(() -> pp.postProcessBeanFactory(beanFactory))
.withMessageContaining("bar")
.withMessageContaining("SingletonBeanConfig")
.withMessageContaining(TestBean.class.getName());
}
@Test // gh-25430
@@ -429,12 +431,12 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
beanFactory.getBean(Bar.class))
.withMessageContaining("OverridingSingletonBeanConfig.foo")
.withMessageContaining(ExtendedFoo.class.getName())
.withMessageContaining(Foo.class.getName())
.withMessageContaining("InvalidOverridingSingletonBeanConfig");
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> beanFactory.getBean(Bar.class))
.withMessageContaining("OverridingSingletonBeanConfig.foo")
.withMessageContaining(ExtendedFoo.class.getName())
.withMessageContaining(Foo.class.getName())
.withMessageContaining("InvalidOverridingSingletonBeanConfig");
}
@Test // SPR-15384
@@ -944,8 +946,8 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfig.class));
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
beanFactory.preInstantiateSingletons();
beanFactory.preInstantiateSingletons();
beanFactory.getBean(ServiceBean.class);
}
@@ -958,8 +960,8 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithDefaultMethods.class));
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
beanFactory.preInstantiateSingletons();
beanFactory.preInstantiateSingletons();
beanFactory.getBean(ServiceBean.class);
}
@@ -972,11 +974,25 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithDefaultMethods.class.getName()));
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class.getName()));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
beanFactory.preInstantiateSingletons();
beanFactory.preInstantiateSingletons();
beanFactory.getBean(ServiceBean.class);
}
@Test
void testConfigWithFailingInit() { // gh-23343
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(beanFactory);
beanFactory.addBeanPostProcessor(bpp);
beanFactory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor());
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithFailingInit.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(beanFactory::preInstantiateSingletons);
assertThat(beanFactory.containsSingleton("configClass")).isFalse();
assertThat(beanFactory.containsSingleton("provider")).isFalse();
}
@Test
void testCircularDependency() {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
@@ -985,16 +1001,17 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("configClass1", new RootBeanDefinition(A.class));
beanFactory.registerBeanDefinition("configClass2", new RootBeanDefinition(AStrich.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
beanFactory::preInstantiateSingletons)
.withMessageContaining("Circular reference");
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(beanFactory::preInstantiateSingletons)
.withMessageContaining("Circular reference");
}
@Test
void testCircularDependencyWithApplicationContext() {
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
new AnnotationConfigApplicationContext(A.class, AStrich.class))
.withMessageContaining("Circular reference");
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> new AnnotationConfigApplicationContext(A.class, AStrich.class))
.withMessageContaining("Circular reference");
}
@Test
@@ -1048,9 +1065,7 @@ class ConfigurationClassPostProcessorTests {
void testCollectionArgumentOnBeanMethod() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class, TestBean.class);
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans).hasSize(1);
assertThat(bean.testBeans.get(0)).isSameAs(ctx.getBean(TestBean.class));
assertThat(bean.testBeans).containsExactly(ctx.getBean(TestBean.class));
ctx.close();
}
@@ -1058,8 +1073,7 @@ class ConfigurationClassPostProcessorTests {
void testEmptyCollectionArgumentOnBeanMethod() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class);
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans.isEmpty()).isTrue();
assertThat(bean.testBeans).isEmpty();
ctx.close();
}
@@ -1067,9 +1081,7 @@ class ConfigurationClassPostProcessorTests {
void testMapArgumentOnBeanMethod() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class, DummyRunnable.class);
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans).hasSize(1);
assertThat(bean.testBeans.values().iterator().next()).isSameAs(ctx.getBean(Runnable.class));
assertThat(bean.testBeans).hasSize(1).containsValue(ctx.getBean(Runnable.class));
ctx.close();
}
@@ -1077,8 +1089,7 @@ class ConfigurationClassPostProcessorTests {
void testEmptyMapArgumentOnBeanMethod() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class);
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans.isEmpty()).isTrue();
assertThat(bean.testBeans).isEmpty();
ctx.close();
}
@@ -1086,9 +1097,7 @@ class ConfigurationClassPostProcessorTests {
void testCollectionInjectionFromSameConfigurationClass() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionInjectionConfiguration.class);
CollectionInjectionConfiguration bean = ctx.getBean(CollectionInjectionConfiguration.class);
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans).hasSize(1);
assertThat(bean.testBeans.get(0)).isSameAs(ctx.getBean(TestBean.class));
assertThat(bean.testBeans).containsExactly(ctx.getBean(TestBean.class));
ctx.close();
}
@@ -1096,25 +1105,21 @@ class ConfigurationClassPostProcessorTests {
void testMapInjectionFromSameConfigurationClass() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapInjectionConfiguration.class);
MapInjectionConfiguration bean = ctx.getBean(MapInjectionConfiguration.class);
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans).hasSize(1);
assertThat(bean.testBeans.get("testBean")).isSameAs(ctx.getBean(Runnable.class));
assertThat(bean.testBeans).containsOnly(Map.entry("testBean", ctx.getBean(Runnable.class)));
ctx.close();
}
@Test
void testBeanLookupFromSameConfigurationClass() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(BeanLookupConfiguration.class);
BeanLookupConfiguration bean = ctx.getBean(BeanLookupConfiguration.class);
assertThat(bean.getTestBean()).isNotNull();
assertThat(bean.getTestBean()).isSameAs(ctx.getBean(TestBean.class));
assertThat(ctx.getBean(BeanLookupConfiguration.class).getTestBean()).isSameAs(ctx.getBean(TestBean.class));
ctx.close();
}
@Test
void testNameClashBetweenConfigurationClassAndBean() {
assertThatExceptionOfType(BeanDefinitionStoreException.class)
.isThrownBy(() -> new AnnotationConfigApplicationContext(MyTestBean.class).getBean("myTestBean", TestBean.class));
.isThrownBy(() -> new AnnotationConfigApplicationContext(MyTestBean.class).getBean("myTestBean", TestBean.class));
}
@Test
@@ -1131,11 +1136,11 @@ class ConfigurationClassPostProcessorTests {
@Order(1)
static class SingletonBeanConfig {
public @Bean Foo foo() {
@Bean public Foo foo() {
return new Foo();
}
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(foo());
}
}
@@ -1143,11 +1148,11 @@ class ConfigurationClassPostProcessorTests {
@Configuration(proxyBeanMethods = false)
static class NonEnhancedSingletonBeanConfig {
public @Bean Foo foo() {
@Bean public Foo foo() {
return new Foo();
}
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(foo());
}
}
@@ -1155,11 +1160,13 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class StaticSingletonBeanConfig {
public static @Bean Foo foo() {
@Bean
public static Foo foo() {
return new Foo();
}
public static @Bean Bar bar() {
@Bean
public static Bar bar() {
return new Bar(foo());
}
}
@@ -1168,11 +1175,11 @@ class ConfigurationClassPostProcessorTests {
@Order(2)
static class OverridingSingletonBeanConfig {
public @Bean ExtendedFoo foo() {
@Bean public ExtendedFoo foo() {
return new ExtendedFoo();
}
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(foo());
}
}
@@ -1180,7 +1187,7 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class OverridingAgainSingletonBeanConfig {
public @Bean ExtendedAgainFoo foo() {
@Bean public ExtendedAgainFoo foo() {
return new ExtendedAgainFoo();
}
}
@@ -1188,7 +1195,7 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class InvalidOverridingSingletonBeanConfig {
public @Bean Foo foo() {
@Bean public Foo foo() {
return new Foo();
}
}
@@ -1200,11 +1207,11 @@ class ConfigurationClassPostProcessorTests {
@Order(1)
static class SingletonBeanConfig {
public @Bean Foo foo() {
@Bean public Foo foo() {
return new Foo();
}
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(foo());
}
}
@@ -1213,11 +1220,11 @@ class ConfigurationClassPostProcessorTests {
@Order(2)
static class OverridingSingletonBeanConfig {
public @Bean ExtendedFoo foo() {
@Bean public ExtendedFoo foo() {
return new ExtendedFoo();
}
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(foo());
}
}
@@ -1233,11 +1240,11 @@ class ConfigurationClassPostProcessorTests {
public SingletonBeanConfig(ConfigWithOrderedInnerClasses other) {
}
public @Bean Foo foo() {
@Bean public Foo foo() {
return new Foo();
}
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(foo());
}
}
@@ -1250,11 +1257,11 @@ class ConfigurationClassPostProcessorTests {
other.getObject();
}
public @Bean ExtendedFoo foo() {
@Bean public ExtendedFoo foo() {
return new ExtendedFoo();
}
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(foo());
}
}
@@ -1281,7 +1288,7 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class UnloadedConfig {
public @Bean Foo foo() {
@Bean public Foo foo() {
return new Foo();
}
}
@@ -1289,7 +1296,7 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class LoadedConfig {
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(new Foo());
}
}
@@ -1598,7 +1605,7 @@ class ConfigurationClassPostProcessorTests {
public static class WildcardWithGenericExtendsConfiguration {
@Bean
public Repository<? extends Object> genericRepo() {
public Repository<?> genericRepo() {
return new Repository<String>();
}
@@ -1707,7 +1714,7 @@ class ConfigurationClassPostProcessorTests {
}
@Configuration
public static abstract class AbstractConfig {
public abstract static class AbstractConfig {
@Bean
public ServiceBean serviceBean() {
@@ -1758,7 +1765,6 @@ class ConfigurationClassPostProcessorTests {
}
public interface DefaultMethodsConfig extends BaseDefaultMethods {
}
@Configuration
@@ -1779,6 +1785,29 @@ class ConfigurationClassPostProcessorTests {
}
}
@Configuration
public static class ConcreteConfigWithFailingInit implements DefaultMethodsConfig, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Bean
@Override
public ServiceBeanProvider provider() {
return new ServiceBeanProvider();
}
@PostConstruct
public void validate() {
beanFactory.getBean("provider");
throw new IllegalStateException();
}
}
@Primary
public static class ServiceBeanProvider {
@@ -1891,7 +1920,7 @@ class ConfigurationClassPostProcessorTests {
}
}
static abstract class FooFactory {
abstract static class FooFactory {
abstract DependingFoo createFoo(BarArgument bar);
}
@@ -2010,7 +2039,7 @@ class ConfigurationClassPostProcessorTests {
}
@Configuration
static abstract class BeanLookupConfiguration {
abstract static class BeanLookupConfiguration {
@Bean
public TestBean thing() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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.
@@ -204,7 +204,7 @@ class ConfigurationClassProcessingTests {
BeanFactory factory = initBeanFactory(ConfigWithNullReference.class);
TestBean foo = factory.getBean("foo", TestBean.class);
assertThat(factory.getBean("bar").equals(null)).isTrue();
assertThat(factory.getBean("bar")).isEqualTo(null);
assertThat(foo.getSpouse()).isNull();
}
@@ -426,7 +426,7 @@ class ConfigurationClassProcessingTests {
@Configuration
static class ConfigWithFinalBean {
public final @Bean TestBean testBean() {
@Bean public final TestBean testBean() {
return new TestBean();
}
}
@@ -435,7 +435,7 @@ class ConfigurationClassProcessingTests {
@Configuration
static class SimplestPossibleConfig {
public @Bean String stringBean() {
@Bean public String stringBean() {
return "foo";
}
}
@@ -444,11 +444,11 @@ class ConfigurationClassProcessingTests {
@Configuration
static class ConfigWithNonSpecificReturnTypes {
public @Bean Object stringBean() {
@Bean public Object stringBean() {
return "foo";
}
public @Bean FactoryBean<?> factoryBean() {
@Bean public FactoryBean<?> factoryBean() {
ListFactoryBean fb = new ListFactoryBean();
fb.setSourceList(Arrays.asList("element1", "element2"));
return fb;
@@ -459,13 +459,13 @@ class ConfigurationClassProcessingTests {
@Configuration
static class ConfigWithPrototypeBean {
public @Bean TestBean foo() {
@Bean public TestBean foo() {
TestBean foo = new SpousyTestBean("foo");
foo.setSpouse(bar());
return foo;
}
public @Bean TestBean bar() {
@Bean public TestBean bar() {
TestBean bar = new SpousyTestBean("bar");
bar.setSpouse(baz());
return bar;
@@ -605,15 +605,15 @@ class ConfigurationClassProcessingTests {
void register(GenericApplicationContext ctx) {
ctx.registerBean("spouse", TestBean.class,
() -> new TestBean("functional"));
Supplier<TestBean> testBeanSupplier = () -> new TestBean(ctx.getBean("spouse", TestBean.class));
ctx.registerBean(TestBean.class,
testBeanSupplier,
Supplier<TestBean> testBeanSupplier =
() -> new TestBean(ctx.getBean("spouse", TestBean.class));
ctx.registerBean(TestBean.class, testBeanSupplier,
bd -> bd.setPrimary(true));
}
@Bean
public NestedTestBean nestedTestBean(TestBean testBean) {
return new NestedTestBean(testBean.getSpouse().getName());
public NestedTestBean nestedTestBean(TestBean spouse) {
return new NestedTestBean(spouse.getSpouse().getName());
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,8 @@
package org.springframework.scheduling.concurrent;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -52,8 +52,8 @@ class ConcurrentTaskExecutorTests extends AbstractSchedulingTaskExecutorTests {
@AfterEach
void shutdownExecutor() {
for (Runnable task : concurrentExecutor.shutdownNow()) {
if (task instanceof RunnableFuture) {
((RunnableFuture<?>) task).cancel(true);
if (task instanceof Future) {
((Future<?>) task).cancel(true);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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.
@@ -101,7 +101,7 @@ class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
@Test
@SuppressWarnings("deprecation")
void scheduleOneTimeFailingTaskWithoutErrorHandler() throws Exception {
void scheduleOneTimeFailingTaskWithoutErrorHandler() {
TestTask task = new TestTask(this.testName, 0);
Future<?> future = scheduler.schedule(task, new Date());
assertThatExceptionOfType(ExecutionException.class).isThrownBy(() -> future.get(1000, TimeUnit.MILLISECONDS));
@@ -147,7 +147,7 @@ class ThreadPoolTaskSchedulerTests extends AbstractSchedulingTaskExecutorTests {
catch (InterruptedException ex) {
throw new IllegalStateException(ex);
}
assertThat(latch.getCount()).as("latch did not count down,").isEqualTo(0);
assertThat(latch.getCount()).as("latch did not count down").isEqualTo(0);
}
@@ -1984,7 +1984,7 @@ class DataBinderTests {
.withMessageContaining("DataBinder is already initialized - call setAutoGrowCollectionLimit before other configuration methods");
}
@Test // SPR-15009
@Test // SPR-15009
void setCustomMessageCodesResolverBeforeInitializeBindingResultForBeanPropertyAccess() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@@ -2001,7 +2001,7 @@ class DataBinderTests {
assertThat(((BeanWrapper) binder.getInternalBindingResult().getPropertyAccessor()).getAutoGrowCollectionLimit()).isEqualTo(512);
}
@Test // SPR-15009
@Test // SPR-15009
void setCustomMessageCodesResolverBeforeInitializeBindingResultForDirectFieldAccess() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@@ -2055,7 +2055,7 @@ class DataBinderTests {
.withMessageContaining("DataBinder is already initialized with MessageCodesResolver");
}
@Test // gh-24347
@Test // gh-24347
void overrideBindingResultType() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@@ -78,8 +78,7 @@ public class MethodValidationTests {
ac.close();
}
@Test // gh-29782
@SuppressWarnings("unchecked")
@Test // gh-29782
public void testMethodValidationPostProcessorForInterfaceOnlyProxy() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MethodValidationPostProcessor.class);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -36,6 +36,7 @@ import org.springframework.util.ReflectionUtils;
*
* @author Juergen Hoeller
* @author Rossen Stoyanchev
* @author Sam Brannen
* @since 4.2.3
*/
public final class MethodIntrospector {
@@ -75,6 +76,7 @@ public final class MethodIntrospector {
if (result != null) {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (bridgedMethod == specificMethod || bridgedMethod == method ||
bridgedMethod.equals(specificMethod) || bridgedMethod.equals(method) ||
metadataLookup.inspect(bridgedMethod) == null) {
methodMap.put(specificMethod, result);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -98,7 +98,9 @@ public final class ReactiveTypeDescriptor {
*/
public Object getEmptyValue() {
Assert.state(this.emptySupplier != null, "Empty values not supported");
return this.emptySupplier.get();
Object emptyValue = this.emptySupplier.get();
Assert.notNull(emptyValue, "Invalid null return value from emptySupplier");
return emptyValue;
}
/**
@@ -130,7 +132,7 @@ public final class ReactiveTypeDescriptor {
/**
* Descriptor for a reactive type that can produce 0..N values.
* Descriptor for a reactive type that can produce {@code 0..N} values.
* @param type the reactive type
* @param emptySupplier a supplier of an empty-value instance of the reactive type
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -647,7 +647,7 @@ public class ResolvableType implements Serializable {
* @param nestingLevel the required nesting level, indexed from 1 for the
* current type, 2 for the first nested generic, 3 for the second and so on
* @param typeIndexesPerLevel a map containing the generic index for a given
* nesting level (may be {@code null})
* nesting level (can be {@code null})
* @return a {@code ResolvableType} for the nested level, or {@link #NONE}
*/
public ResolvableType getNested(int nestingLevel, @Nullable Map<Integer, Integer> typeIndexesPerLevel) {
@@ -679,7 +679,7 @@ public class ResolvableType implements Serializable {
* generic is returned.
* <p>If no generic is available at the specified indexes {@link #NONE} is returned.
* @param indexes the indexes that refer to the generic parameter
* (may be omitted to return the first generic)
* (can be omitted to return the first generic)
* @return a {@code ResolvableType} for the specified generic, or {@link #NONE}
* @see #hasGenerics()
* @see #getGenerics()
@@ -747,7 +747,7 @@ public class ResolvableType implements Serializable {
* Convenience method that will {@link #getGenerics() get} and
* {@link #resolve() resolve} generic parameters.
* @return an array of resolved generic parameters (the resulting array
* will never be {@code null}, but it may contain {@code null} elements})
* will never be {@code null}, but it may contain {@code null} elements)
* @see #getGenerics()
* @see #resolve()
*/
@@ -782,7 +782,7 @@ public class ResolvableType implements Serializable {
* Convenience method that will {@link #getGeneric(int...) get} and
* {@link #resolve() resolve} a specific generic parameter.
* @param indexes the indexes that refer to the generic parameter
* (may be omitted to return the first generic)
* (can be omitted to return the first generic)
* @return a resolved {@link Class} or {@code null}
* @see #getGeneric(int...)
* @see #resolve()
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,7 +38,7 @@ public abstract class OrderUtils {
/** Cache marker for a non-annotated Class. */
private static final Object NOT_ANNOTATED = new Object();
private static final String JAVAX_PRIORITY_ANNOTATION = "jakarta.annotation.Priority";
private static final String JAKARTA_PRIORITY_ANNOTATION = "jakarta.annotation.Priority";
/** Cache for @Order value (or NOT_ANNOTATED marker) per Class. */
static final Map<AnnotatedElement, Object> orderCache = new ConcurrentReferenceHashMap<>(64);
@@ -124,7 +124,7 @@ public abstract class OrderUtils {
if (orderAnnotation.isPresent()) {
return orderAnnotation.getInt(MergedAnnotation.VALUE);
}
MergedAnnotation<?> priorityAnnotation = annotations.get(JAVAX_PRIORITY_ANNOTATION);
MergedAnnotation<?> priorityAnnotation = annotations.get(JAKARTA_PRIORITY_ANNOTATION);
if (priorityAnnotation.isPresent()) {
return priorityAnnotation.getInt(MergedAnnotation.VALUE);
}
@@ -139,7 +139,7 @@ public abstract class OrderUtils {
*/
@Nullable
public static Integer getPriority(Class<?> type) {
return MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY).get(JAVAX_PRIORITY_ANNOTATION)
return MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY).get(JAKARTA_PRIORITY_ANNOTATION)
.getValue(MergedAnnotation.VALUE, Integer.class).orElse(null);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -95,20 +95,19 @@ public interface Decoder<T> {
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) throws DecodingException {
CompletableFuture<T> future = decodeToMono(Mono.just(buffer), targetType, mimeType, hints).toFuture();
Assert.state(future.isDone(), "DataBuffer decoding should have completed.");
Assert.state(future.isDone(), "DataBuffer decoding should have completed");
Throwable failure;
try {
return future.get();
}
catch (ExecutionException ex) {
failure = ex.getCause();
Throwable cause = ex.getCause();
throw (cause instanceof CodecException codecException ? codecException :
new DecodingException("Failed to decode: " + (cause != null ? cause.getMessage() : ex), cause));
}
catch (InterruptedException ex) {
failure = ex;
throw new DecodingException("Interrupted during decode", ex);
}
throw (failure instanceof CodecException codecException ? codecException :
new DecodingException("Failed to decode: " + failure.getMessage(), failure));
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -76,10 +76,11 @@ public class ResourceDecoder extends AbstractDataBufferDecoder<Resource> {
}
Class<?> clazz = elementType.toClass();
String filename = hints != null ? (String) hints.get(FILENAME_HINT) : null;
String filename = (hints != null ? (String) hints.get(FILENAME_HINT) : null);
if (clazz == InputStreamResource.class) {
return new InputStreamResource(new ByteArrayInputStream(bytes)) {
@Override
@Nullable
public String getFilename() {
return filename;
}
@@ -92,6 +93,7 @@ public class ResourceDecoder extends AbstractDataBufferDecoder<Resource> {
else if (Resource.class.isAssignableFrom(clazz)) {
return new ByteArrayResource(bytes) {
@Override
@Nullable
public String getFilename() {
return filename;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,8 +52,6 @@ import org.springframework.util.ObjectUtils;
@SuppressWarnings("serial")
public class TypeDescriptor implements Serializable {
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
private static final Map<Class<?>, TypeDescriptor> commonTypesCache = new HashMap<>(32);
private static final Class<?>[] CACHED_COMMON_TYPES = {
@@ -84,7 +82,7 @@ public class TypeDescriptor implements Serializable {
public TypeDescriptor(MethodParameter methodParameter) {
this.resolvableType = ResolvableType.forMethodParameter(methodParameter);
this.type = this.resolvableType.resolve(methodParameter.getNestedParameterType());
this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ?
this.annotatedElement = AnnotatedElementAdapter.from(methodParameter.getParameterIndex() == -1 ?
methodParameter.getMethodAnnotations() : methodParameter.getParameterAnnotations());
}
@@ -96,7 +94,7 @@ public class TypeDescriptor implements Serializable {
public TypeDescriptor(Field field) {
this.resolvableType = ResolvableType.forField(field);
this.type = this.resolvableType.resolve(field.getType());
this.annotatedElement = new AnnotatedElementAdapter(field.getAnnotations());
this.annotatedElement = AnnotatedElementAdapter.from(field.getAnnotations());
}
/**
@@ -109,7 +107,7 @@ public class TypeDescriptor implements Serializable {
Assert.notNull(property, "Property must not be null");
this.resolvableType = ResolvableType.forMethodParameter(property.getMethodParameter());
this.type = this.resolvableType.resolve(property.getType());
this.annotatedElement = new AnnotatedElementAdapter(property.getAnnotations());
this.annotatedElement = AnnotatedElementAdapter.from(property.getAnnotations());
}
/**
@@ -125,7 +123,7 @@ public class TypeDescriptor implements Serializable {
public TypeDescriptor(ResolvableType resolvableType, @Nullable Class<?> type, @Nullable Annotation[] annotations) {
this.resolvableType = resolvableType;
this.type = (type != null ? type : resolvableType.toClass());
this.annotatedElement = new AnnotatedElementAdapter(annotations);
this.annotatedElement = AnnotatedElementAdapter.from(annotations);
}
@@ -733,18 +731,26 @@ public class TypeDescriptor implements Serializable {
* @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class)
* @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class)
*/
private class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
private static final class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
private static final AnnotatedElementAdapter EMPTY = new AnnotatedElementAdapter(new Annotation[0]);
@Nullable
private final Annotation[] annotations;
public AnnotatedElementAdapter(@Nullable Annotation[] annotations) {
private AnnotatedElementAdapter(Annotation[] annotations) {
this.annotations = annotations;
}
private static AnnotatedElementAdapter from(@Nullable Annotation[] annotations) {
if (annotations == null || annotations.length == 0) {
return EMPTY;
}
return new AnnotatedElementAdapter(annotations);
}
@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
for (Annotation annotation : getAnnotations()) {
for (Annotation annotation : this.annotations) {
if (annotation.annotationType() == annotationClass) {
return true;
}
@@ -756,7 +762,7 @@ public class TypeDescriptor implements Serializable {
@Nullable
@SuppressWarnings("unchecked")
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
for (Annotation annotation : getAnnotations()) {
for (Annotation annotation : this.annotations) {
if (annotation.annotationType() == annotationClass) {
return (T) annotation;
}
@@ -766,7 +772,7 @@ public class TypeDescriptor implements Serializable {
@Override
public Annotation[] getAnnotations() {
return (this.annotations != null ? this.annotations.clone() : EMPTY_ANNOTATION_ARRAY);
return (isEmpty() ? this.annotations : this.annotations.clone());
}
@Override
@@ -775,7 +781,7 @@ public class TypeDescriptor implements Serializable {
}
public boolean isEmpty() {
return ObjectUtils.isEmpty(this.annotations);
return (this.annotations.length == 0);
}
@Override
@@ -791,7 +797,7 @@ public class TypeDescriptor implements Serializable {
@Override
public String toString() {
return TypeDescriptor.this.toString();
return Arrays.toString(this.annotations);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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,7 @@ final class ObjectToObjectConverter implements ConditionalGenericConverter {
@Nullable
private static Executable getValidatedExecutable(Class<?> targetClass, Class<?> sourceClass) {
Executable executable = conversionExecutableCache.get(targetClass);
if (isApplicable(executable, sourceClass)) {
if (executable != null && isApplicable(executable, sourceClass)) {
return executable;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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,7 +62,7 @@ import org.springframework.util.Assert;
*/
public abstract class DataBufferUtils {
private final static Log logger = LogFactory.getLog(DataBufferUtils.class);
private static final Log logger = LogFactory.getLog(DataBufferUtils.class);
private static final Consumer<DataBuffer> RELEASE_CONSUMER = DataBufferUtils::release;
@@ -745,7 +745,7 @@ public abstract class DataBufferUtils {
*/
private static class SingleByteMatcher implements NestedMatcher {
static SingleByteMatcher NEWLINE_MATCHER = new SingleByteMatcher(new byte[] {10});
static final SingleByteMatcher NEWLINE_MATCHER = new SingleByteMatcher(new byte[] {10});
private final byte[] delimiter;
@@ -784,7 +784,7 @@ public abstract class DataBufferUtils {
/**
* Base class for a {@link NestedMatcher}.
*/
private static abstract class AbstractNestedMatcher implements NestedMatcher {
private abstract static class AbstractNestedMatcher implements NestedMatcher {
private final byte[] delimiter;
@@ -990,7 +990,7 @@ public abstract class DataBufferUtils {
DataBuffer.ByteBufferIterator iterator = dataBuffer.writableByteBuffers();
Assert.state(iterator.hasNext(), "No ByteBuffer available");
ByteBuffer byteBuffer = iterator.next();
Attachment attachment = new Attachment(dataBuffer, iterator);
Attachment attachment = new Attachment(dataBuffer, iterator);
this.channel.read(byteBuffer, this.position.get(), attachment, this);
}
@@ -999,7 +999,7 @@ public abstract class DataBufferUtils {
attachment.iterator().close();
DataBuffer dataBuffer = attachment.dataBuffer();
if (this.state.get().equals(State.DISPOSED)) {
if (this.state.get() == State.DISPOSED) {
release(dataBuffer);
closeChannel(this.channel);
return;
@@ -1030,13 +1030,13 @@ public abstract class DataBufferUtils {
}
@Override
public void failed(Throwable exc, Attachment attachment) {
public void failed(Throwable ex, Attachment attachment) {
attachment.iterator().close();
release(attachment.dataBuffer());
closeChannel(this.channel);
this.state.set(State.DISPOSED);
this.sink.error(exc);
this.sink.error(ex);
}
private enum State {
@@ -1095,7 +1095,6 @@ public abstract class DataBufferUtils {
public Context currentContext() {
return Context.of(this.sink.contextView());
}
}
@@ -1190,13 +1189,13 @@ public abstract class DataBufferUtils {
}
@Override
public void failed(Throwable exc, Attachment attachment) {
public void failed(Throwable ex, Attachment attachment) {
attachment.iterator().close();
this.sink.next(attachment.dataBuffer());
this.writing.set(false);
this.sink.error(exc);
this.sink.error(ex);
}
@Override
@@ -1205,9 +1204,6 @@ public abstract class DataBufferUtils {
}
private record Attachment(ByteBuffer byteBuffer, DataBuffer dataBuffer, DataBuffer.ByteBufferIterator iterator) {}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -557,7 +557,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
String rootDirPath = determineRootDir(locationPattern);
String subPattern = locationPattern.substring(rootDirPath.length());
Resource[] rootDirResources = getResources(rootDirPath);
Set<Resource> result = new LinkedHashSet<>(16);
Set<Resource> result = new LinkedHashSet<>(64);
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
URL rootDirUrl = rootDirResource.getURL();
@@ -706,7 +706,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
// The Sun JRE does not return a slash here, but BEA JRockit does.
rootEntryPath = rootEntryPath + "/";
}
Set<Resource> result = new LinkedHashSet<>(8);
Set<Resource> result = new LinkedHashSet<>(64);
for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
JarEntry entry = entries.nextElement();
String entryPath = entry.getName();
@@ -756,7 +756,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern)
throws IOException {
Set<Resource> result = new LinkedHashSet<>();
Set<Resource> result = new LinkedHashSet<>(64);
URI rootDirUri;
try {
rootDirUri = rootDirResource.getURI();
@@ -865,7 +865,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
* @see PathMatcher#match(String, String)
*/
protected Set<Resource> findAllModulePathResources(String locationPattern) throws IOException {
Set<Resource> result = new LinkedHashSet<>(16);
Set<Resource> result = new LinkedHashSet<>(64);
// Skip scanning the module path when running in a native image.
if (NativeDetector.inNativeImage()) {
@@ -966,7 +966,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
private final String rootPath;
private final Set<Resource> resources = new LinkedHashSet<>();
private final Set<Resource> resources = new LinkedHashSet<>(64);
public PatternVirtualFileVisitor(String rootPath, String subPattern, PathMatcher pathMatcher) {
this.subPattern = subPattern;
@@ -997,7 +997,6 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
else if ("toString".equals(methodName)) {
return toString();
}
throw new IllegalStateException("Unexpected method invocation: " + method);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,8 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.function.Supplier;
import org.springframework.lang.Nullable;
/**
* Default "no op" {@code ApplicationStartup} implementation.
*
@@ -52,6 +54,7 @@ class DefaultApplicationStartup implements ApplicationStartup {
}
@Override
@Nullable
public Long getParentId() {
return null;
}
@@ -73,7 +76,6 @@ class DefaultApplicationStartup implements ApplicationStartup {
@Override
public void end() {
}
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,10 +21,10 @@ import java.util.function.Consumer;
import java.util.function.Supplier;
import org.springframework.core.metrics.StartupStep;
import org.springframework.lang.NonNull;
/**
* {@link StartupStep} implementation for the Java Flight Recorder.
*
* <p>This variant delegates to a {@link FlightRecorderStartupEvent JFR event extension}
* to collect and record data in Java Flight Recorder.
*
@@ -114,12 +114,12 @@ class FlightRecorderStartupStep implements StartupStep {
add(key, value.get());
}
@NonNull
@Override
public Iterator<Tag> iterator() {
return new TagsIterator();
}
private class TagsIterator implements Iterator<Tag> {
private int idx = 0;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -73,20 +73,20 @@ public abstract class AbstractTypeHierarchyTraversingFilter implements TypeFilte
// Optimization to avoid creating ClassReader for superclass.
Boolean superClassMatch = matchSuperClass(superClassName);
if (superClassMatch != null) {
if (superClassMatch.booleanValue()) {
if (superClassMatch) {
return true;
}
}
else {
// Need to read superclass to determine a match...
try {
if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
if (match(superClassName, metadataReaderFactory)) {
return true;
}
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not read superclass [" + metadata.getSuperClassName() +
logger.debug("Could not read superclass [" + superClassName +
"] of type-filtered class [" + metadata.getClassName() + "]");
}
}
@@ -99,7 +99,7 @@ public abstract class AbstractTypeHierarchyTraversingFilter implements TypeFilte
// Optimization to avoid creating ClassReader for superclass
Boolean interfaceMatch = matchInterface(ifc);
if (interfaceMatch != null) {
if (interfaceMatch.booleanValue()) {
if (interfaceMatch) {
return true;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -243,7 +243,7 @@ public abstract class ClassUtils {
* style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State").
* @param name the name of the Class
* @param classLoader the class loader to use
* (may be {@code null}, which indicates the default class loader)
* (can be {@code null}, which indicates the default class loader)
* @return a class instance for the supplied name
* @throws ClassNotFoundException if the class was not found
* @throws LinkageError if the class file could not be loaded
@@ -314,7 +314,7 @@ public abstract class ClassUtils {
* the exceptions thrown in case of class loading failure.
* @param className the name of the Class
* @param classLoader the class loader to use
* (may be {@code null}, which indicates the default class loader)
* (can be {@code null}, which indicates the default class loader)
* @return a class instance for the supplied name
* @throws IllegalArgumentException if the class name was not resolvable
* (that is, the class could not be found or the class file could not be loaded)
@@ -348,7 +348,7 @@ public abstract class ClassUtils {
* one of its dependencies is not present or cannot be loaded.
* @param className the name of the class to check
* @param classLoader the class loader to use
* (may be {@code null} which indicates the default class loader)
* (can be {@code null} which indicates the default class loader)
* @return whether the specified class is present (including all of its
* superclasses and interfaces)
* @throws IllegalStateException if the corresponding class is resolvable but
@@ -375,7 +375,7 @@ public abstract class ClassUtils {
* Check whether the given class is visible in the given ClassLoader.
* @param clazz the class to check (typically an interface)
* @param classLoader the ClassLoader to check against
* (may be {@code null} in which case this method will always return {@code true})
* (can be {@code null} in which case this method will always return {@code true})
*/
public static boolean isVisible(Class<?> clazz, @Nullable ClassLoader classLoader) {
if (classLoader == null) {
@@ -399,7 +399,7 @@ public abstract class ClassUtils {
* i.e. whether it is loaded by the given ClassLoader or a parent of it.
* @param clazz the class to analyze
* @param classLoader the ClassLoader to potentially cache metadata in
* (may be {@code null} which indicates the system class loader)
* (can be {@code null} which indicates the system class loader)
*/
public static boolean isCacheSafe(Class<?> clazz, @Nullable ClassLoader classLoader) {
Assert.notNull(clazz, "Class must not be null");
@@ -663,7 +663,7 @@ public abstract class ClassUtils {
* in the given collection.
* <p>Basically like {@code AbstractCollection.toString()}, but stripping
* the "class "/"interface " prefix before every class name.
* @param classes a Collection of Class objects (may be {@code null})
* @param classes a Collection of Class objects (can be {@code null})
* @return a String of form "[com.foo.Bar, com.foo.Baz]"
* @see java.util.AbstractCollection#toString()
*/
@@ -718,7 +718,7 @@ public abstract class ClassUtils {
* <p>If the class itself is an interface, it gets returned as sole interface.
* @param clazz the class to analyze for interfaces
* @param classLoader the ClassLoader that the interfaces need to be visible in
* (may be {@code null} when accepting all declared interfaces)
* (can be {@code null} when accepting all declared interfaces)
* @return all interfaces that the given object implements as an array
*/
public static Class<?>[] getAllInterfacesForClass(Class<?> clazz, @Nullable ClassLoader classLoader) {
@@ -753,7 +753,7 @@ public abstract class ClassUtils {
* <p>If the class itself is an interface, it gets returned as sole interface.
* @param clazz the class to analyze for interfaces
* @param classLoader the ClassLoader that the interfaces need to be visible in
* (may be {@code null} when accepting all declared interfaces)
* (can be {@code null} when accepting all declared interfaces)
* @return all interfaces that the given object implements as a Set
*/
public static Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz, @Nullable ClassLoader classLoader) {
@@ -1082,7 +1082,7 @@ public abstract class ClassUtils {
* fully qualified interface/class name + "." + method name.
* @param method the method
* @param clazz the clazz that the method is being invoked on
* (may be {@code null} to indicate the method's declaring class)
* (can be {@code null} to indicate the method's declaring class)
* @return the qualified name of the method
* @since 4.3.4
*/
@@ -1163,7 +1163,7 @@ public abstract class ClassUtils {
* @param clazz the clazz to analyze
* @param methodName the name of the method
* @param paramTypes the parameter types of the method
* (may be {@code null} to indicate any signature)
* (can be {@code null} to indicate any signature)
* @return the method (never {@code null})
* @throws IllegalStateException if the method has not been found
* @see Class#getMethod
@@ -1202,7 +1202,7 @@ public abstract class ClassUtils {
* @param clazz the clazz to analyze
* @param methodName the name of the method
* @param paramTypes the parameter types of the method
* (may be {@code null} to indicate any signature)
* (can be {@code null} to indicate any signature)
* @return the method, or {@code null} if not found
* @see Class#getMethod
*/
@@ -1291,13 +1291,14 @@ public abstract class ClassUtils {
* implementation will fall back to returning the originally provided method.
* @param method the method to be invoked, which may come from an interface
* @param targetClass the target class for the current invocation
* (may be {@code null} or may not even implement the method)
* (can be {@code null} or may not even implement the method)
* @return the specific target method, or the original method if the
* {@code targetClass} does not implement it
* @see #getInterfaceMethodIfPossible(Method, Class)
*/
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
if (targetClass != null && targetClass != method.getDeclaringClass() && isOverridable(method, targetClass)) {
if (targetClass != null && targetClass != method.getDeclaringClass() &&
(isOverridable(method, targetClass) || !method.getDeclaringClass().isAssignableFrom(targetClass))) {
try {
if (Modifier.isPublic(method.getModifiers())) {
try {
@@ -0,0 +1,112 @@
/*
* Copyright 2002-2024 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.core;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.core.MethodIntrospector.MetadataLookup;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.core.annotation.MergedAnnotations.SearchStrategy.TYPE_HIERARCHY;
/**
* Tests for {@link MethodIntrospector}.
*
* @author Sam Brannen
* @since 5.3.34
*/
class MethodIntrospectorTests {
@Test // gh-32586
void selectMethodsAndClearDeclaredMethodsCacheBetweenInvocations() {
Class<?> targetType = ActualController.class;
// Preconditions for this use case.
assertThat(targetType).isPublic();
assertThat(targetType.getSuperclass()).isPackagePrivate();
MetadataLookup<String> metadataLookup = (MetadataLookup<String>) method -> {
if (MergedAnnotations.from(method, TYPE_HIERARCHY).isPresent(Mapped.class)) {
return method.getName();
}
return null;
};
// Start with a clean slate.
ReflectionUtils.clearCache();
// Round #1
Map<Method, String> methods = MethodIntrospector.selectMethods(targetType, metadataLookup);
assertThat(methods.values()).containsExactlyInAnyOrder("update", "delete");
// Simulate ConfigurableApplicationContext#refresh() which clears the
// ReflectionUtils#declaredMethodsCache but NOT the BridgeMethodResolver#cache.
// As a consequence, ReflectionUtils.getDeclaredMethods(...) will return a
// new set of methods that are logically equivalent to but not identical
// to (in terms of object identity) any bridged methods cached in the
// BridgeMethodResolver cache.
ReflectionUtils.clearCache();
// Round #2
methods = MethodIntrospector.selectMethods(targetType, metadataLookup);
assertThat(methods.values()).containsExactlyInAnyOrder("update", "delete");
}
@Retention(RetentionPolicy.RUNTIME)
@interface Mapped {
}
interface Controller {
void unmappedMethod();
@Mapped
void update();
@Mapped
void delete();
}
// Must NOT be public.
abstract static class AbstractController implements Controller {
@Override
public void unmappedMethod() {
}
@Override
public void delete() {
}
}
// MUST be public.
public static class ActualController extends AbstractController {
@Override
public void update() {
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -51,14 +51,14 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
*/
class PathMatchingResourcePatternResolverTests {
private static final String[] CLASSES_IN_CORE_IO_SUPPORT = { "EncodedResource.class",
private static final String[] CLASSES_IN_CORE_IO_SUPPORT = {"EncodedResource.class",
"LocalizedResourceHelper.class", "PathMatchingResourcePatternResolver.class", "PropertiesLoaderSupport.class",
"PropertiesLoaderUtils.class", "ResourceArrayPropertyEditor.class", "ResourcePatternResolver.class",
"ResourcePatternUtils.class", "SpringFactoriesLoader.class" };
"ResourcePatternUtils.class", "SpringFactoriesLoader.class"};
private static final String[] TEST_CLASSES_IN_CORE_IO_SUPPORT = { "PathMatchingResourcePatternResolverTests.class" };
private static final String[] TEST_CLASSES_IN_CORE_IO_SUPPORT = {"PathMatchingResourcePatternResolverTests.class"};
private static final String[] CLASSES_IN_REACTOR_UTIL_ANNOTATION = { "NonNull.class", "NonNullApi.class", "Nullable.class" };
private static final String[] CLASSES_IN_REACTOR_UTIL_ANNOTATION = {"NonNull.class", "NonNullApi.class", "Nullable.class"};
private PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
@@ -200,7 +200,7 @@ class PathMatchingResourcePatternResolverTests {
}
@Test
void usingFileProtocolWithoutWildcardInPatternAndEndingInSlashStarStar() {
void usingFileProtocolWithoutWildcardInPatternAndEndingInSlashStarStar() {
Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath();
String pattern = String.format("file:%s/scanned-resources/**", testResourcesDir);
String pathPrefix = ".+?resources/";
@@ -329,8 +329,8 @@ class PathMatchingResourcePatternResolverTests {
}
private String getPath(Resource resource) {
// Tests fail if we use resouce.getURL().getPath(). They would also fail on Mac OS when
// using resouce.getURI().getPath() if the resource paths are not Unicode normalized.
// Tests fail if we use resource.getURL().getPath(). They would also fail on macOS when
// using resource.getURI().getPath() if the resource paths are not Unicode normalized.
//
// On the JVM, all tests should pass when using resouce.getFile().getPath(); however,
// we use FileSystemResource#getPath since this test class is sometimes run within a
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ import org.springframework.dao.DataRetrievalFailureException;
/**
* Data access exception thrown when a result set did not have the correct column count,
* for example when expecting a single column but getting 0 or more than 1 columns.
* for example when expecting a single column but getting 0 or more than 1 column.
*
* @author Juergen Hoeller
* @since 2.0
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ import org.springframework.dao.IncorrectUpdateSemanticsDataAccessException;
/**
* Exception thrown when a JDBC update affects an unexpected number of rows.
* Typically we expect an update to affect a single row, meaning it's an
* Typically, we expect an update to affect a single row, meaning it is an
* error if it affects multiple rows.
*
* @author Rod Johnson
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2024 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 @@ package org.springframework.jdbc.core;
*
* <p>This interface allows you to signal the end of a batch rather than
* having to determine the exact batch size upfront. Batch size is still
* being honored but it is now the maximum size of the batch.
* being honored, but it is now the maximum size of the batch.
*
* <p>The {@link #isBatchExhausted} method is called after each call to
* {@link #setValues} to determine whether there were some values added,
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -128,8 +128,8 @@ public abstract class RdbmsOperation implements InitializingBean {
/**
* Set the maximum number of rows for this RDBMS operation. This is important
* for processing subsets of large result sets, avoiding to read and hold
* the entire result set in the database or in the JDBC driver.
* for processing subsets of large result sets, in order to avoid reading and
* holding the entire result set in the database or in the JDBC driver.
* <p>Default is -1, indicating to use the driver's default.
* @see org.springframework.jdbc.core.JdbcTemplate#setMaxRows
*/
@@ -175,7 +175,7 @@ public abstract class RdbmsOperation implements InitializingBean {
public void setUpdatableResults(boolean updatableResults) {
if (isCompiled()) {
throw new InvalidDataAccessApiUsageException(
"The updateableResults flag must be set before the operation is compiled");
"The updatableResults flag must be set before the operation is compiled");
}
this.updatableResults = updatableResults;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 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,7 @@ import org.springframework.jdbc.core.SqlParameter;
/**
* Superclass for object abstractions of RDBMS stored procedures.
* This class is abstract and it is intended that subclasses will provide a typed
* This class is abstract, and it is intended that subclasses will provide a typed
* method for invocation that delegates to the supplied {@link #execute} method.
*
* <p>The inherited {@link #setSql sql} property is the name of the stored procedure
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 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.
@@ -25,9 +25,9 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.lang.Nullable;
/**
* Registry for custom {@link org.springframework.jdbc.support.SQLExceptionTranslator} instances associated with
* specific databases allowing for overriding translation based on values contained in the configuration file
* named "sql-error-codes.xml".
* Registry for custom {@link SQLExceptionTranslator} instances associated with
* specific databases allowing for overriding translation based on values
* contained in the configuration file named "sql-error-codes.xml".
*
* @author Thomas Risberg
* @since 3.1.1
@@ -38,7 +38,7 @@ public final class CustomSQLExceptionTranslatorRegistry {
private static final Log logger = LogFactory.getLog(CustomSQLExceptionTranslatorRegistry.class);
/**
* Keep track of a single instance so we can return it to classes that request it.
* Keep track of a single instance, so we can return it to classes that request it.
*/
private static final CustomSQLExceptionTranslatorRegistry instance = new CustomSQLExceptionTranslatorRegistry();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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.
@@ -219,7 +219,7 @@ public abstract class JdbcUtils {
return NumberUtils.convertNumberToTargetClass(number, Integer.class);
}
else {
// e.g. on Postgres: getObject returns a PGObject but we need a String
// e.g. on Postgres: getObject returns a PGObject, but we need a String
return rs.getString(index);
}
}
@@ -229,14 +229,14 @@ public abstract class JdbcUtils {
try {
return rs.getObject(index, requiredType);
}
catch (AbstractMethodError err) {
logger.debug("JDBC driver does not implement JDBC 4.1 'getObject(int, Class)' method", err);
}
catch (SQLFeatureNotSupportedException ex) {
catch (SQLFeatureNotSupportedException | AbstractMethodError ex) {
logger.debug("JDBC driver does not support JDBC 4.1 'getObject(int, Class)' method", ex);
}
catch (SQLException ex) {
logger.debug("JDBC driver has limited support for JDBC 4.1 'getObject(int, Class)' method", ex);
if (logger.isDebugEnabled()) {
logger.debug("JDBC driver has limited support for 'getObject(int, Class)' with column type: " +
requiredType.getName(), ex);
}
}
// Corresponding SQL types for JSR-310 / Joda-Time types, left up
@@ -415,14 +415,14 @@ public abstract class JdbcUtils {
}
/**
* Return whether the given JDBC driver supports JDBC 2.0 batch updates.
* Return whether the given JDBC driver supports JDBC batch updates.
* <p>Typically invoked right before execution of a given set of statements:
* to decide whether the set of SQL statements should be executed through
* the JDBC 2.0 batch mechanism or simply in a traditional one-by-one fashion.
* the JDBC batch mechanism or simply in a traditional one-by-one fashion.
* <p>Logs a warning if the "supportsBatchUpdates" methods throws an exception
* and simply returns {@code false} in that case.
* @param con the Connection to check
* @return whether JDBC 2.0 batch updates are supported
* @return whether JDBC batch updates are supported
* @see java.sql.DatabaseMetaData#supportsBatchUpdates()
*/
public static boolean supportsBatchUpdates(Connection con) {
@@ -492,8 +492,8 @@ public abstract class JdbcUtils {
/**
* Determine the column name to use. The column name is determined based on a
* lookup using ResultSetMetaData.
* <p>This method implementation takes into account recent clarifications
* expressed in the JDBC 4.0 specification:
* <p>This method's implementation takes into account clarifications expressed
* in the JDBC 4.0 specification:
* <p><i>columnLabel - the label for the column specified with the SQL AS clause.
* If the SQL AS clause was not specified, then the label is the name of the column</i>.
* @param resultSetMetaData the current meta-data to use
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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,7 +67,7 @@ public class SQLErrorCodesFactory {
private static final Log logger = LogFactory.getLog(SQLErrorCodesFactory.class);
/**
* Keep track of a single instance so we can return it to classes that request it.
* Keep track of a single instance, so we can return it to classes that request it.
* Lazily initialized in order to avoid making {@code SQLErrorCodesFactory} constructor
* reachable on native images when not needed.
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -284,6 +284,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* @see #CACHE_CONNECTION
* @see #CACHE_SESSION
* @see #CACHE_CONSUMER
* @see #CACHE_AUTO
* @see #setCacheLevelName
* @see #setTransactionManager
*/
@@ -570,8 +571,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
if (this.taskExecutor == null) {
this.taskExecutor = createDefaultTaskExecutor();
}
else if (this.taskExecutor instanceof SchedulingTaskExecutor ste &&
ste.prefersShortLivedTasks() &&
else if (this.taskExecutor instanceof SchedulingTaskExecutor ste && ste.prefersShortLivedTasks() &&
this.maxMessagesPerTask == Integer.MIN_VALUE) {
// TaskExecutor indicated a preference for short-lived tasks. According to
// setMaxMessagesPerTask javadoc, we'll use 10 message per task in this case
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -217,7 +217,7 @@ public abstract class JmsUtils {
try {
session.commit();
}
catch (jakarta.jms.TransactionInProgressException | jakarta.jms.IllegalStateException ex) {
catch (jakarta.jms.TransactionInProgressException ex) {
// Ignore -> can only happen in case of a JTA transaction.
}
}
@@ -232,7 +232,7 @@ public abstract class JmsUtils {
try {
session.rollback();
}
catch (jakarta.jms.TransactionInProgressException | jakarta.jms.IllegalStateException ex) {
catch (jakarta.jms.TransactionInProgressException ex) {
// Ignore -> can only happen in case of a JTA transaction.
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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,7 +19,6 @@ package org.springframework.jms.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import jakarta.jms.JMSException;
import jakarta.jms.MessageListener;
import org.junit.jupiter.api.Test;
@@ -103,24 +102,20 @@ class EnableJmsTests extends AbstractJmsAnnotationDrivenTests {
}
@Test
@SuppressWarnings("resource")
void containerAreStartedByDefault() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
EnableJmsDefaultContainerFactoryConfig.class, DefaultBean.class);
JmsListenerContainerTestFactory factory =
context.getBean(JmsListenerContainerTestFactory.class);
JmsListenerContainerTestFactory factory = context.getBean(JmsListenerContainerTestFactory.class);
MessageListenerTestContainer container = factory.getListenerContainers().get(0);
assertThat(container.isAutoStartup()).isTrue();
assertThat(container.isStarted()).isTrue();
}
@Test
@SuppressWarnings("resource")
void containerCanBeStarterViaTheRegistry() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
EnableJmsAutoStartupFalseConfig.class, DefaultBean.class);
JmsListenerContainerTestFactory factory =
context.getBean(JmsListenerContainerTestFactory.class);
JmsListenerContainerTestFactory factory = context.getBean(JmsListenerContainerTestFactory.class);
MessageListenerTestContainer container = factory.getListenerContainers().get(0);
assertThat(container.isAutoStartup()).isFalse();
assertThat(container.isStarted()).isFalse();
@@ -131,13 +126,13 @@ class EnableJmsTests extends AbstractJmsAnnotationDrivenTests {
@Override
@Test
void jmsHandlerMethodFactoryConfiguration() throws JMSException {
void jmsHandlerMethodFactoryConfiguration() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
EnableJmsHandlerMethodFactoryConfig.class, ValidationBean.class);
assertThatExceptionOfType(ListenerExecutionFailedException.class).isThrownBy(() ->
testJmsHandlerMethodFactoryConfiguration(context))
.withCauseInstanceOf(MethodArgumentNotValidException.class);
assertThatExceptionOfType(ListenerExecutionFailedException.class)
.isThrownBy(() -> testJmsHandlerMethodFactoryConfiguration(context))
.withCauseInstanceOf(MethodArgumentNotValidException.class);
}
@Override
@@ -159,19 +154,20 @@ class EnableJmsTests extends AbstractJmsAnnotationDrivenTests {
@Test
void composedJmsListeners() {
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
EnableJmsDefaultContainerFactoryConfig.class, ComposedJmsListenersBean.class)) {
JmsListenerContainerTestFactory simpleFactory = context.getBean("jmsListenerContainerFactory",
JmsListenerContainerTestFactory.class);
EnableJmsDefaultContainerFactoryConfig.class, ComposedJmsListenersBean.class)) {
JmsListenerContainerTestFactory simpleFactory =
context.getBean("jmsListenerContainerFactory", JmsListenerContainerTestFactory.class);
assertThat(simpleFactory.getListenerContainers()).hasSize(2);
MethodJmsListenerEndpoint first = (MethodJmsListenerEndpoint) simpleFactory.getListenerContainer(
"first").getEndpoint();
MethodJmsListenerEndpoint first = (MethodJmsListenerEndpoint)
simpleFactory.getListenerContainer("first").getEndpoint();
assertThat(first.getId()).isEqualTo("first");
assertThat(first.getDestination()).isEqualTo("orderQueue");
assertThat(first.getConcurrency()).isNull();
MethodJmsListenerEndpoint second = (MethodJmsListenerEndpoint) simpleFactory.getListenerContainer(
"second").getEndpoint();
MethodJmsListenerEndpoint second = (MethodJmsListenerEndpoint)
simpleFactory.getListenerContainer("second").getEndpoint();
assertThat(second.getId()).isEqualTo("second");
assertThat(second.getDestination()).isEqualTo("billingQueue");
assertThat(second.getConcurrency()).isEqualTo("2-10");
@@ -179,12 +175,11 @@ class EnableJmsTests extends AbstractJmsAnnotationDrivenTests {
}
@Test
@SuppressWarnings("resource")
void unknownFactory() {
// not found
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
new AnnotationConfigApplicationContext(EnableJmsSampleConfig.class, CustomBean.class))
.withMessageContaining("customFactory");
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> new AnnotationConfigApplicationContext(EnableJmsSampleConfig.class, CustomBean.class))
.withMessageContaining("customFactory");
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2024 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.
@@ -28,15 +28,13 @@ public class JmsListenerContainerTestFactory implements JmsListenerContainerFact
private boolean autoStartup = true;
private final Map<String, MessageListenerTestContainer> listenerContainers =
new LinkedHashMap<>();
private final Map<String, MessageListenerTestContainer> listenerContainers = new LinkedHashMap<>();
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
public List<MessageListenerTestContainer> getListenerContainers() {
return new ArrayList<>(this.listenerContainers.values());
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -264,8 +264,9 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
* @see jakarta.persistence.spi.PersistenceUnitInfo#getNonJtaDataSource()
* @see #setPersistenceUnitManager
*/
public void setDataSource(DataSource dataSource) {
this.internalPersistenceUnitManager.setDataSourceLookup(new SingleDataSourceLookup(dataSource));
public void setDataSource(@Nullable DataSource dataSource) {
this.internalPersistenceUnitManager.setDataSourceLookup(
dataSource != null ? new SingleDataSourceLookup(dataSource) : null);
this.internalPersistenceUnitManager.setDefaultDataSource(dataSource);
}
@@ -281,8 +282,9 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
* @see jakarta.persistence.spi.PersistenceUnitInfo#getJtaDataSource()
* @see #setPersistenceUnitManager
*/
public void setJtaDataSource(DataSource jtaDataSource) {
this.internalPersistenceUnitManager.setDataSourceLookup(new SingleDataSourceLookup(jtaDataSource));
public void setJtaDataSource(@Nullable DataSource jtaDataSource) {
this.internalPersistenceUnitManager.setDataSourceLookup(
jtaDataSource != null ? new SingleDataSourceLookup(jtaDataSource) : null);
this.internalPersistenceUnitManager.setDefaultJtaDataSource(jtaDataSource);
}
@@ -427,6 +429,7 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
}
@Override
@Nullable
public DataSource getDataSource() {
if (this.persistenceUnitInfo != null) {
return (this.persistenceUnitInfo.getJtaDataSource() != null ?
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 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.
@@ -28,28 +28,18 @@ import jakarta.persistence.spi.PersistenceProvider;
* shared JPA EntityManagerFactory in a Spring application context; the
* EntityManagerFactory can then be passed to JPA-based DAOs via
* dependency injection. Note that switching to a JNDI lookup or to a
* {@link LocalContainerEntityManagerFactoryBean}
* definition is just a matter of configuration!
* {@link LocalContainerEntityManagerFactoryBean} definition based on the
* JPA container contract is just a matter of configuration!
*
* <p>Configuration settings are usually read from a {@code META-INF/persistence.xml}
* config file, residing in the class path, according to the JPA standalone bootstrap
* contract. Additionally, most JPA providers will require a special VM agent
* (specified on JVM startup) that allows them to instrument application classes.
* See the Java Persistence API specification and your provider documentation
* for setup details.
*
* <p>This EntityManagerFactory bootstrap is appropriate for standalone applications
* which solely use JPA for data access. If you want to set up your persistence
* provider for an external DataSource and/or for global transactions which span
* multiple resources, you will need to either deploy it into a full Jakarta EE
* application server and access the deployed EntityManagerFactory via JNDI,
* or use Spring's {@link LocalContainerEntityManagerFactoryBean} with appropriate
* configuration for local setup according to JPA's container contract.
* contract. See the Java Persistence API specification and your persistence provider
* documentation for setup details. Additionally, JPA properties can also be added
* on this FactoryBean via {@link #setJpaProperties}/{@link #setJpaPropertyMap}.
*
* <p><b>Note:</b> This FactoryBean has limited configuration power in terms of
* what configuration it is able to pass to the JPA provider. If you need more
* flexible configuration, for example passing a Spring-managed JDBC DataSource
* to the JPA provider, consider using Spring's more powerful
* the configuration that it is able to pass to the JPA provider. If you need
* more flexible configuration options, consider using Spring's more powerful
* {@link LocalContainerEntityManagerFactoryBean} instead.
*
* @author Juergen Hoeller
@@ -0,0 +1 @@
Args = -H:ServiceLoaderFeatureExcludeServices=org.hibernate.bytecode.spi.BytecodeProvider
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -70,13 +70,15 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
private boolean suppressClose;
/** Override auto-commit state?. */
private @Nullable Boolean autoCommit;
@Nullable
private Boolean autoCommit;
/** Wrapped Connection. */
private final AtomicReference<Connection> target = new AtomicReference<>();
/** Proxy Connection. */
private @Nullable Connection connection;
@Nullable
private Connection connection;
private final Mono<? extends Connection> connectionEmitter;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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,7 +63,6 @@ import jakarta.servlet.http.Part;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.LinkedCaseInsensitiveMap;
@@ -1020,7 +1019,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
}
}
private static String encodeCookies(@NonNull Cookie... cookies) {
private static String encodeCookies(Cookie... cookies) {
return Arrays.stream(cookies)
.map(c -> c.getName() + '=' + (c.getValue() == null ? "" : c.getValue()))
.collect(Collectors.joining("; "));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -292,6 +292,11 @@ public class MockHttpServletResponse implements HttpServletResponse {
doAddHeaderValue(HttpHeaders.CONTENT_LENGTH, contentLength, true);
}
/**
* Get the length of the content body from the HTTP Content-Length header.
* @return the value of the Content-Length header
* @see #setContentLength(int)
*/
public int getContentLength() {
return (int) this.contentLength;
}
@@ -742,7 +747,7 @@ public class MockHttpServletResponse implements HttpServletResponse {
@Override
public void setStatus(int status) {
if (!this.isCommitted()) {
if (!isCommitted()) {
this.status = status;
}
}
@@ -752,6 +757,9 @@ public class MockHttpServletResponse implements HttpServletResponse {
return this.status;
}
/**
* Return the error message used when calling {@link HttpServletResponse#sendError(int, String)}.
*/
@Nullable
public String getErrorMessage() {
return this.errorMessage;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,7 +35,6 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestContextAnnotationUtils;
@@ -315,7 +314,6 @@ public class SqlScriptsTestExecutionListener extends AbstractTestExecutionListen
}
}
@NonNull
private ResourceDatabasePopulator createDatabasePopulator(MergedSqlConfig mergedSqlConfig) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.setSqlScriptEncoding(mergedSqlConfig.getEncoding());
@@ -31,7 +31,6 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Integration tests for {@link MockMvcWebConnection}.
*
@@ -64,14 +63,15 @@ public class MockMvcWebConnectionTests {
public void contextPathEmpty() throws IOException {
this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, this.webClient, ""));
// Empty context path (root context) should not match to a URL with a context path
assertThatExceptionOfType(FailingHttpStatusCodeException.class).isThrownBy(() ->
this.webClient.getPage("http://localhost/context/a"))
.satisfies(ex -> assertThat(ex.getStatusCode()).isEqualTo(404));
assertThatExceptionOfType(FailingHttpStatusCodeException.class)
.isThrownBy(() -> this.webClient.getPage("http://localhost/context/a"))
.satisfies(ex -> assertThat(ex.getStatusCode()).isEqualTo(404));
this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, this.webClient));
// No context is the same providing an empty context path
assertThatExceptionOfType(FailingHttpStatusCodeException.class).isThrownBy(() ->
this.webClient.getPage("http://localhost/context/a"))
.satisfies(ex -> assertThat(ex.getStatusCode()).isEqualTo(404));
assertThatExceptionOfType(FailingHttpStatusCodeException.class)
.isThrownBy(() -> this.webClient.getPage("http://localhost/context/a"))
.satisfies(ex -> assertThat(ex.getStatusCode()).isEqualTo(404));
}
@Test
@@ -84,8 +84,9 @@ public class MockMvcWebConnectionTests {
@Test
public void infiniteForward() {
this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, this.webClient, ""));
assertThatIllegalStateException().isThrownBy(() -> this.webClient.getPage("http://localhost/infiniteForward"))
.withMessage("Forwarded 100 times in a row, potential infinite forward loop");
assertThatIllegalStateException()
.isThrownBy(() -> this.webClient.getPage("http://localhost/infiniteForward"))
.withMessage("Forwarded 100 times in a row, potential infinite forward loop");
}
@Test
@@ -32,7 +32,6 @@ import org.springframework.mock.web.MockHttpServletResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link MockWebResponseBuilder}.
*
@@ -55,8 +54,6 @@ public class MockWebResponseBuilderTests {
}
// --- constructor
@Test
public void constructorWithNullWebRequest() {
assertThatIllegalArgumentException().isThrownBy(() ->
@@ -66,12 +63,10 @@ public class MockWebResponseBuilderTests {
@Test
public void constructorWithNullResponse() throws Exception {
assertThatIllegalArgumentException().isThrownBy(() ->
new MockWebResponseBuilder(0L, new WebRequest(new URL("http://company.example:80/test/this/here")), null));
new MockWebResponseBuilder(0L,
new WebRequest(new URL("http://company.example:80/test/this/here")), null));
}
// --- build
@Test
public void buildContent() throws Exception {
this.response.getWriter().write("expected content");
@@ -124,8 +119,7 @@ public class MockWebResponseBuilderTests {
.endsWith("; Secure; HttpOnly");
}
// SPR-14169
@Test
@Test // SPR-14169
public void buildResponseHeadersNullDomainDefaulted() throws Exception {
Cookie cookie = new Cookie("cookieA", "valueA");
this.response.addCookie(cookie);
@@ -52,6 +52,7 @@ class MockMvcHtmlUnitDriverBuilderTests {
private HtmlUnitDriver driver;
MockMvcHtmlUnitDriverBuilderTests(WebApplicationContext wac) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@@ -48,6 +48,7 @@ class WebConnectionHtmlUnitDriverTests {
@Mock
private WebConnection connection;
@BeforeEach
void setup() throws Exception {
given(this.connection.getResponse(any(WebRequest.class))).willThrow(new IOException(""));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2024 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,10 +44,11 @@ public class IncorrectUpdateSemanticsDataAccessException extends InvalidDataAcce
super(msg, cause);
}
/**
* Return whether data was updated.
* If this method returns false, there's nothing to roll back.
* <p>The default implementation always returns true.
* If this method returns {@code false}, there is nothing to roll back.
* <p>The default implementation always returns {@code true}.
* This can be overridden in subclasses.
*/
public boolean wasDataUpdated() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 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.
@@ -42,11 +42,6 @@ import org.springframework.util.StringValueResolver;
* If none found on the target class, the interface that the invoked method
* has been called through (in case of a JDK proxy) will be checked.
*
* <p>This implementation caches attributes by method after they are first used.
* If it is ever desirable to allow dynamic changing of transaction attributes
* (which is very unlikely), caching could be made configurable. Caching is
* desirable because of the cost of evaluating rollback rules.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 1.1
@@ -95,7 +90,7 @@ public abstract class AbstractFallbackTransactionAttributeSource
* Determine the transaction attribute for this method invocation.
* <p>Defaults to the class's transaction attribute if no method attribute is found.
* @param method the method for the current invocation (never {@code null})
* @param targetClass the target class for this invocation (may be {@code null})
* @param targetClass the target class for this invocation (can be {@code null})
* @return a TransactionAttribute for this method, or {@code null} if the method
* is not transactional
*/
@@ -106,27 +101,15 @@ public abstract class AbstractFallbackTransactionAttributeSource
return null;
}
// First, see if we have a cached value.
Object cacheKey = getCacheKey(method, targetClass);
TransactionAttribute cached = this.attributeCache.get(cacheKey);
if (cached != null) {
// Value will either be canonical value indicating there is no transaction attribute,
// or an actual transaction attribute.
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
return null;
}
else {
return cached;
}
return (cached != NULL_TRANSACTION_ATTRIBUTE ? cached : null);
}
else {
// We need to work it out.
TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
// Put it in the cache.
if (txAttr == null) {
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
else {
if (txAttr != null) {
String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
if (txAttr instanceof DefaultTransactionAttribute dta) {
dta.setDescriptor(methodIdentification);
@@ -137,6 +120,9 @@ public abstract class AbstractFallbackTransactionAttributeSource
}
this.attributeCache.put(cacheKey, txAttr);
}
else {
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
return txAttr;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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.
@@ -57,7 +57,7 @@ public interface TransactionAttributeSource {
* Return the transaction attribute for the given method,
* or {@code null} if the method is non-transactional.
* @param method the method to introspect
* @param targetClass the target class (may be {@code null},
* @param targetClass the target class (can be {@code null},
* in which case the declaring class of the method must be used)
* @return the matching transaction attribute, or {@code null} if none found
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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,7 +77,7 @@ final class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointc
* {@link ClassFilter} that delegates to {@link TransactionAttributeSource#isCandidateClass}
* for filtering classes whose methods are not worth searching to begin with.
*/
private class TransactionAttributeSourceClassFilter implements ClassFilter {
private final class TransactionAttributeSourceClassFilter implements ClassFilter {
@Override
public boolean matches(Class<?> clazz) {
@@ -89,6 +89,7 @@ final class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointc
return (transactionAttributeSource == null || transactionAttributeSource.isCandidateClass(clazz));
}
@Nullable
private TransactionAttributeSource getTransactionAttributeSource() {
return transactionAttributeSource;
}
@@ -96,7 +97,7 @@ final class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointc
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof TransactionAttributeSourceClassFilter that &&
ObjectUtils.nullSafeEquals(transactionAttributeSource, that.getTransactionAttributeSource())));
ObjectUtils.nullSafeEquals(getTransactionAttributeSource(), that.getTransactionAttributeSource())));
}
@Override
@@ -106,9 +107,8 @@ final class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointc
@Override
public String toString() {
return TransactionAttributeSourceClassFilter.class.getName() + ": " + transactionAttributeSource;
return TransactionAttributeSourceClassFilter.class.getName() + ": " + getTransactionAttributeSource();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -18,7 +18,6 @@ package org.springframework.http;
import java.io.Serializable;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
/**
@@ -80,8 +79,8 @@ final class DefaultHttpStatusCode implements HttpStatusCode, Comparable<HttpStat
@Override
public int compareTo(@NonNull HttpStatusCode o) {
return Integer.compare(this.value, o.value());
public int compareTo(HttpStatusCode other) {
return Integer.compare(this.value, other.value());
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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.
@@ -113,6 +113,7 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
}
@Override
@Nullable
protected Long getContentLength(Resource resource, @Nullable MediaType contentType) throws IOException {
// Don't try to determine contentLength on InputStreamResource - cannot be read afterwards...
// Note: custom InputStreamResource subclasses could provide a pre-calculated content length!

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