Compare commits

..

86 Commits

Author SHA1 Message Date
Spring Builds 13b31c7976 Release v5.3.29 2023-07-13 07:44:31 +00:00
Juergen Hoeller 53319381d0 Polishing
(cherry picked from commit c873a597c7)
2023-07-12 19:25:26 +02:00
Juergen Hoeller e1e7fa489b Upgrade to Reactor 2020.0.34 (and Tomcat 9.0.78)
Closes gh-30873
2023-07-12 09:41:42 +02:00
Brian Clozel e1351a5cb5 Update OS version in CI image 2023-07-12 08:28:18 +02:00
Juergen Hoeller ac94d2bd54 Polishing
(cherry picked from commit f19433f2d8)
2023-07-11 18:40:40 +02:00
Juergen Hoeller 16fd70ae35 Avoid illegal reflective access in ContextOverridingClassLoader
Closes gh-22791

(cherry picked from commit 0b02a5e073)
2023-07-11 18:36:19 +02:00
Juergen Hoeller c1bf09952b Improve diagnostics for LinkageError in case of ClassLoader mismatch
Closes gh-25940
2023-07-11 18:36:12 +02:00
Juergen Hoeller 9e7ee0cb8e Deprecate setAllowResultAccessAfterCompletion and document it as broken
Closes gh-26557
2023-07-11 18:36:02 +02:00
rstoyanchev 1cd994d3a8 Encapsulate full path initialization 2023-07-11 11:45:23 +01:00
Juergen Hoeller 3d28c024c5 Handle JDBC warnings in case of a statement exception as well
Closes gh-23106

(cherry picked from commit 3b899fe7e2)
2023-07-10 17:25:05 +02:00
Juergen Hoeller 3444892aea Polishing
(cherry picked from commit a17cf742b2)
2023-07-09 17:13:49 +02:00
Juergen Hoeller 14a7983eef Tolerate isCandidateClass call with null as annotation type
Closes gh-30842

(cherry picked from commit a102cd5f32)
2023-07-09 17:05:54 +02:00
Sam Brannen 0a4f290f0f Polish DefaultSingletonBeanRegistryTests 2023-07-09 16:11:45 +02:00
Sam Brannen 6ea4d3794a Further simplify DefaultSingletonBeanRegistry.isDependent()
See gh-30841
2023-07-09 16:11:41 +02:00
bnbakp0582 85eec5d344 Simplify DefaultSingletonBeanRegistry.isDependent()
Move `alreadySeen` handling out of for-loop.

Closes gh-30841
2023-07-09 16:11:32 +02:00
Juergen Hoeller 0961bc546a Polishing
(cherry picked from commit 0b7a24fc14)
2023-07-08 01:10:31 +02:00
Juergen Hoeller f2df10c7fe Polishing 2023-07-07 13:26:06 +02:00
Sam Brannen 03f1fabbdb Improve assertions in DefaultConversionServiceTests
Specifically, we now check the actual type of a converted collection in
various assertions to ensure that converters adhere to their contracts.
2023-07-06 13:11:35 +02:00
Sam Brannen 2ada2b77f6 Update Javadoc for ObjectUtils.nullSafeConciseToString()
See gh-30811
2023-07-06 12:29:46 +02:00
Juergen Hoeller d5380b3070 Expand tests for array to Collection/Set/List interface
See gh-28048
2023-07-05 21:12:36 +02:00
Sam Brannen a3907a64e5 Support arrays, collections, & maps in ObjectUtils.nullSafeConciseToString()
Prior to this commit, there was no explicit support for arrays,
collections, and maps in nullSafeConciseToString(). This lead to string
representations such as the following, regardless of whether the array,
collection, or map was empty.

- char[]@1623b78d
- java.util.ImmutableCollections$List12@74fe5c40
- java.util.ImmutableCollections$MapN@10e31a9a

This commit introduces explicit support for arrays, collections, and
maps in nullSafeConciseToString(), which results in the following
empty/non-empty string representations.

- array: {} / {...}
- collection: [] / [...]
- map: {} / {...}

The reason a string representation of an array uses "{}" instead of
"[]" (like in Arrays.toString(...)) is that
ObjectUtils.nullSafeToString(<array>) already follows that convention,
and the implementation of nullSafeConciseToString() aligns with that
for the sake of consistency.

Closes gh-30811
2023-07-05 17:15:15 +02:00
Juergen Hoeller dd16e012ba Clarify ReactiveTransactionManager exception declarations
Avoid misleading "throws TransactionException" declarations but preserve javadoc "@throws" notes for specific exceptions (with reactive propagation semantics).

Closes gh-30817
2023-07-05 12:15:05 +02:00
Juergen Hoeller d4cd358c76 Discuss JdbcTransactionManager vs DataSourceTransactionManager
Closes gh-30802
2023-07-05 12:14:48 +02:00
Juergen Hoeller da814e01c7 Polishing 2023-07-04 21:52:36 +02:00
Juergen Hoeller 5d4c2846d9 Polishing 2023-07-04 16:43:29 +02:00
Juergen Hoeller a3daee6ad8 Make File/Path tests pass on Windows
See gh-30806
2023-07-04 16:38:10 +02:00
Juergen Hoeller 5614e5bc18 Restore full representation of rejected value in FieldError.toString()
We would preferably use ObjectUtils.nullSafeConciseToString(rejectedValue) here but revert to the full nullSafeToString representation for strict backwards compatibility (programmatic toString calls as well as exception messages).

Closes gh-30799

(cherry picked from commit 1dc9dffc70)
2023-07-04 16:13:48 +02:00
Juergen Hoeller 6dde13f597 Refresh cached value after unexpected mismatch (e.g. null vs non-null)
In addition to the previously addressed removal of bean definitions, this is able to deal with prototype factory methods returning non-null after null or also null after non-null. Stale cached values are getting refreshed rather than bypassed.

Closes gh-30794

(cherry picked from commit 0226580773)
2023-07-04 16:13:44 +02:00
Sam Brannen c057da23ec Extend supported types in ObjectUtils.nullSafeConciseToString()
This commit extends the list of explicitly supported types in
ObjectUtils.nullSafeConciseToString() with the following.

- Optional
- File
- Path
- InetAddress
- Charset
- Currency
- TimeZone
- ZoneId
- Pattern

Closes gh-30806
2023-07-04 15:01:35 +02:00
Sam Brannen a7f07328ab Add tests for status quo in ObjectUtils.nullSafeConciseToString() 2023-07-04 14:57:57 +02:00
Sam Brannen 45f747fae1 Clean up warnings in tests 2023-07-04 14:34:32 +02:00
Vladyslav Baidak 258bd3f73c Fix typo in Javadoc for BeanDefinitionDsl.kt
Closes gh-30798
2023-07-03 15:53:09 +02:00
Juergen Hoeller 97b95d9d01 Upgrade to Tomcat 9.0.76, Netty 4.1.94, Undertow 2.2.25, Checkstyle 10.12.1 2023-06-30 13:16:57 +02:00
Juergen Hoeller 69827a2f21 Raise beforeCompletion/afterCompletion exception log level to error
Closes gh-30776

(cherry picked from commit f1567fb21a)
2023-06-30 13:16:37 +02:00
Juergen Hoeller ef699b6a9e Align ConcurrentMapCacheManager locking behavior with CaffeineCacheManager
Closes gh-30780

(cherry picked from commit 60865eae4b)
2023-06-30 10:53:21 +02:00
Juergen Hoeller e440eb8365 Consistently handle invocation exceptions in TypeProxyInvocationHandler
Closes gh-30764

(cherry picked from commit 3cb746c358)
2023-06-28 15:48:41 +02:00
Juergen Hoeller 02cbee560d Polishing
(cherry picked from commit 6526e79eea)
2023-06-26 20:06:12 +02:00
Juergen Hoeller 14da1aca2f Adapt no-arg value from interface-based InvocationHandler callback
Closes gh-30756

(cherry picked from commit b77d4d01c5)
2023-06-26 20:06:06 +02:00
Juergen Hoeller ce97342fee Consistently use mutable ArrayList for modulesToInstall vs modules
Closes gh-30751

(cherry picked from commit 062d701ae1)
2023-06-26 12:39:01 +02:00
Sam Brannen 2e51aa250e Update copyright headers 2023-06-22 14:54:43 +02:00
Juergen Hoeller ec2957afc8 Test for supportsEventType mismatch with unrelated event type
See gh-30712
2023-06-21 18:01:15 +02:00
Juergen Hoeller d3df45d8fe Avoid ResolvableType creation for interface/superclass check
See gh-30713

(cherry picked from commit 1dfe737d0e)
2023-06-21 17:46:59 +02:00
Juergen Hoeller 5375f62dc1 Cache hasUnresolvableGenerics result for repeated checks
Closes gh-30713

(cherry picked from commit 93218a06ba)
2023-06-21 13:24:42 +02:00
Juergen Hoeller c7bc40d3ba Ensure Spring LogFactory contains all public methods from Apache LogFactory
Closes gh-30668

(cherry picked from commit 20bbebb299)
2023-06-21 09:58:23 +02:00
Juergen Hoeller 1071778aa9 Fall back to type-based creation if no bean of the given name exists
Closes gh-30683

(cherry picked from commit dff7aa4d4b)
2023-06-17 11:46:48 +02:00
Juergen Hoeller 40a9ae9d14 Recognize error code 2628 as data integrity violation (MSSQL 2019)
Closes gh-30681

(cherry picked from commit c634acd9ff)
2023-06-17 11:44:11 +02:00
Sam Brannen e34a7baeb3 Remove code duplication in RootBeanDefinition 2023-06-15 16:12:31 +02:00
Brian Clozel 62eb9b391d Use Docker hub credentials for CI tasks 2023-06-15 13:20:51 +02:00
Spring Builds ea89bf2c91 Next development version (v5.3.29-SNAPSHOT) 2023-06-15 07:32:26 +00:00
Juergen Hoeller 99ae6e70bb Declare ClassLoader for DeserializingConverter constructor as nullable
Closes gh-30670

(cherry picked from commit b9221656cc)
2023-06-14 22:34:28 +02:00
Juergen Hoeller c2cc55eacc Consider UUID as simple value type with concise toString output
Closes gh-30661

(cherry picked from commit 927d27b121)
2023-06-14 10:47:54 +02:00
Juergen Hoeller 9cff2ace97 Upgrade to Reactor 2020.0.33
Closes gh-30656
2023-06-13 18:40:46 +02:00
Juergen Hoeller 3c2590d339 Document limited isolation level support for concurrent transactions
See gh-29997
2023-06-13 12:57:52 +02:00
Juergen Hoeller 4b55333b0e Document which @Scheduled attributes support SpEL expressions
Closes gh-29290

(cherry picked from commit f8c8873c99)
2023-06-12 13:08:02 +02:00
Juergen Hoeller c27acad616 Specific check for parent of MethodInvocationInfo ClassLoader
See gh-30389
2023-06-12 11:33:54 +02:00
Juergen Hoeller 70be9afdc6 Reuse method cache from original proxy factory (aligned with 6.0.x)
See gh-30616
2023-06-12 11:11:07 +02:00
Juergen Hoeller d4450a8702 Specific check for parent of spring-aop ClassLoader
Closes gh-30389

(cherry picked from commit 0a5aff1b60)
2023-06-12 11:08:37 +02:00
Juergen Hoeller 210e47d65e Polishing 2023-06-08 18:31:31 +02:00
Juergen Hoeller 06411831e8 Consistent ProxyCallbackFilter#equals/hashCode methods
Opaque check in equals instead; no consideration of optimize flag.

Closes gh-30616
2023-06-08 18:31:26 +02:00
Juergen Hoeller 8c7daa807a Polishing 2023-06-07 20:14:41 +02:00
Juergen Hoeller 46d171a8fd Restore creation of plain HashSet/HashMap for direct HashSet/HashMap type
Closes gh-30596

(cherry picked from commit cdc4497664)
2023-06-05 14:03:15 +02:00
Juergen Hoeller 259bd5250d Consistent javadoc references to JdbcTransactionManager 2023-06-05 11:12:38 +02:00
Juergen Hoeller cef046218c Upgrade to Tomcat 9.0.75, Netty 4.1.93, Undertow 2.2.24, EclipseLink 2.7.12 2023-06-04 18:13:00 +02:00
Juergen Hoeller e4bd1344e2 Set and reset shared isolation value within synchronized transaction begin
Since EclipseLink applies a custom transaction isolation value to its shared DatabasePlatform instance, we need to immediately restore the original value after the current value got picked up for JDBC Connection access inside of EclipseLink. In order to not interfere with concurrent transactions, we need to use synchronization around the transaction begin sequence in such a case.

Closes gh-29997
2023-06-04 18:12:40 +02:00
Juergen Hoeller 9decbf2158 Polishing 2023-06-03 00:01:18 +02:00
Juergen Hoeller 268b7a8931 Revise TargetSource implementations for proper nullability
Includes hashCode optimization in AbstractBeanFactoryBasedTargetSource.
Includes ThreadLocal naming fix in ThreadLocalTargetSource.

Closes gh-30576
Closes gh-30581

(cherry picked from commit c68552556f)
2023-06-02 23:48:41 +02:00
Juergen Hoeller 1240fb6b9a Consistently publish events from CompletableFuture
Closes gh-30578

(cherry picked from commit b738a20233)
2023-06-02 23:36:13 +02:00
Brian Clozel 7dae3afb4b Resolve Asciidoctor extensions from Central
This commit upgrades spring-asciidoctor-extensions-block-switch to 0.6.1
as this version is available on Maven Central.
2023-05-30 09:49:23 +02:00
Stephane Nicoll d9de36b5ee Merge branch 'gh-30555' into 5.3.x
Closes gh-30555
2023-05-30 09:18:41 +02:00
Stephane Nicoll 4e696db922 Update copyright year of changed file
See gh-30554
2023-05-30 09:18:06 +02:00
Stefano Cordio 0adad10595 Fix FileSystemUtils::deleteRecursively Javadoc
See gh-30554
2023-05-30 09:17:54 +02:00
Juergen Hoeller 540d0d9345 Avoid Autowired shortcut resolution for NullBean values
Includes getBean documentation against NullBean values.

Closes gh-30485

(cherry picked from commit 8b8d147480)
2023-05-26 11:16:09 +02:00
Brian Clozel 572bbeeba3 Use spring-doc-resources SNAPSHOT version 2023-05-25 20:04:08 +02:00
Brian Clozel 77cd44dd20 Update CI pipeline 2023-05-25 19:13:58 +02:00
rstoyanchev 65e7b4a279 Add ignore rule for cached-antora-playbook.yml
In case of checking out the 5.3.x branch after 6.0.x or main
2023-05-23 15:14:01 +01:00
Sam Brannen 7d95a24573 Make maximum SpEL expression length configurable
Closes gh-30446
2023-05-10 15:12:36 +02:00
Juergen Hoeller 1dbe0ee6db Respect TaskDecorator configuration on DefaultManagedTaskExecutor
Closes gh-30442
2023-05-08 12:18:53 +02:00
Juergen Hoeller 0211016957 Consistent support for MultiValueMap and common Map implementations
Closes gh-30440
2023-05-08 12:13:44 +02:00
Sam Brannen 08fe123930 Introduce Environment.matchesProfiles() for profile expressions
Environment.acceptsProfiles(String...) was deprecated in 5.1 in
conjunction with gh-17063 which introduced a new
acceptsProfiles(Profiles) method to replace it. The deprecated method
only supports OR semantics; whereas, the new method supports profile
expressions. Thus, the goal was to encourage people to use the more
powerful profile expressions instead of the limited OR support with
profile names.

However, there are use cases where it is difficult (if not impossible)
to provide a Profiles instance, and there are use cases where it is
simply preferable to provide profile expressions directly as strings.

To address these issues, this commit introduces a new matchesProfiles()
method in Environment that accepts a var-args list of profile
expressions.

See gh-30206
Closes gh-30226
2023-04-25 19:19:22 +02:00
Sam Brannen 219448796f Polish Environment and StandardEnvironmentTests
See gh-30206
See gh-30226
2023-04-25 19:19:03 +02:00
Sam Brannen 0956c144c9 Polish ProfilesParser internals 2023-04-25 18:56:08 +02:00
Sam Brannen 964950a8b9 Reject null and empty SpEL expressions
Prior to gh-30325, supplying a null reference for a SpEL expression was
effectively equivalent to supplying the String "null" as the
expression. Consequently, evaluation of a null reference expression
always evaluated to a null reference. However, that was accidental
rather than by design.

Due to the introduction of the checkExpressionLength(String) method in
InternalSpelExpressionParser (in conjunction with gh-30325), an attempt
to evaluate a null reference as a SpEL expression now results in a
NullPointerException.

To address both of these issues,
TemplateAwareExpressionParser.parseExpression() and
SpelExpressionParser.parseRaw() now reject null and empty SpEL
expressions.

See gh-30371
Closes gh-30373
2023-04-25 14:31:35 +02:00
Sam Brannen 5afd94f90f Polish SpelParserTests and TemplateExpressionParsingTests 2023-04-25 14:29:01 +02:00
Arjen Poutsma ec5f7644e4 Updated CI image JDK 2023-04-24 13:00:06 +02:00
Arjen Poutsma 0c8ef4c671 Updated sdkmanrc 2023-04-24 12:37:15 +02:00
Spring Builds a851b7309a Next development version (v5.3.28-SNAPSHOT) 2023-04-13 08:57:10 +00:00
133 changed files with 2589 additions and 1559 deletions
+3 -1
View File
@@ -46,4 +46,6 @@ atlassian-ide-plugin.xml
.gradletasknamecache
# VS Code
.vscode/
.vscode/
cached-antora-playbook.yml
+1 -1
View File
@@ -1,3 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=8.0.362-librca
java=8.0.372-librca
+7 -7
View File
@@ -28,8 +28,8 @@ configure(allprojects) { project ->
dependencyManagement {
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.7"
mavenBom "io.netty:netty-bom:4.1.91.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.31"
mavenBom "io.netty:netty-bom:4.1.94.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.34"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
mavenBom "io.rsocket:rsocket-bom:1.1.3"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.51.v20230217"
@@ -128,18 +128,18 @@ configure(allprojects) { project ->
dependency "org.webjars:webjars-locator-core:0.48"
dependency "org.webjars:underscorejs:1.8.3"
dependencySet(group: 'org.apache.tomcat', version: '9.0.73') {
dependencySet(group: 'org.apache.tomcat', version: '9.0.78') {
entry 'tomcat-util'
entry('tomcat-websocket') {
exclude group: "org.apache.tomcat", name: "tomcat-servlet-api"
exclude group: "org.apache.tomcat", name: "tomcat-websocket-api"
}
}
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.73') {
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.78') {
entry 'tomcat-embed-core'
entry 'tomcat-embed-websocket'
}
dependencySet(group: 'io.undertow', version: '2.2.23.Final') {
dependencySet(group: 'io.undertow', version: '2.2.25.Final') {
entry 'undertow-core'
entry('undertow-servlet') {
exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec"
@@ -238,7 +238,7 @@ configure(allprojects) { project ->
dependency "com.ibm.websphere:uow:6.0.2.17"
dependency "com.jamonapi:jamon:2.82"
dependency "joda-time:joda-time:2.10.13"
dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.10"
dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.12"
dependency "org.javamoney:moneta:1.3"
dependency "com.sun.activation:javax.activation:1.2.0"
@@ -340,7 +340,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "10.9.3"
toolVersion = "10.12.1"
configDirectory.set(rootProject.file("src/checkstyle"))
}
+1 -1
View File
@@ -1,4 +1,4 @@
FROM ubuntu:focal-20220922
FROM ubuntu:jammy-20230624
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
+2 -2
View File
@@ -3,10 +3,10 @@ set -e
case "$1" in
java8)
echo "https://github.com/bell-sw/Liberica/releases/download/8u345+1/bellsoft-jdk8u345+1-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/8u372+7/bellsoft-jdk8u372+7-linux-amd64.tar.gz"
;;
java17)
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.4+8/bellsoft-jdk17.0.4+8-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.7+7/bellsoft-jdk17.0.7+7-linux-amd64.tar.gz"
;;
*)
echo $"Unknown java version"
-47
View File
@@ -64,12 +64,6 @@ resource_types:
<<: *docker-resource-source
repository: dpb587/github-status-resource
tag: master
- name: pull-request
type: registry-image
source:
<<: *docker-resource-source
repository: teliaoss/github-pr-resource
tag: v0.23.0
- name: slack-notification
type: registry-image
source:
@@ -111,14 +105,6 @@ resources:
username: ((artifactory-username))
password: ((artifactory-password))
build_name: ((build-name))
- name: git-pull-request
type: pull-request
icon: source-pull
source:
access_token: ((github-ci-pull-request-token))
repository: ((github-repo-name))
base_branch: ((branch))
ignore_paths: ["ci/*"]
- name: repo-status-build
type: github-status-resource
icon: eye-check-outline
@@ -259,37 +245,6 @@ jobs:
<<: *slack-fail-params
- put: repo-status-jdk17-build
params: { state: "success", commit: "git-repo" }
- name: build-pull-requests
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
resource: git-pull-request
trigger: true
version: every
- do:
- put: git-pull-request
params:
path: git-repo
status: pending
- task: build-pr
image: ci-image
file: git-repo/ci/tasks/build-pr.yml
privileged: true
timeout: ((task-timeout))
params:
BRANCH: ((branch))
on_success:
put: git-pull-request
params:
path: git-repo
status: success
on_failure:
put: git-pull-request
params:
path: git-repo
status: failure
- name: stage-milestone
serial: true
plan:
@@ -442,5 +397,3 @@ groups:
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
jobs: ["build-ci-images"]
- name: "pull-requests"
jobs: [ "build-pull-requests" ]
+2
View File
@@ -5,6 +5,8 @@ image_resource:
source:
repository: springio/github-changelog-generator
tag: '0.0.7'
username: ((docker-hub-username))
password: ((docker-hub-password))
inputs:
- name: git-repo
- name: artifactory-repo
+2
View File
@@ -5,6 +5,8 @@ image_resource:
source:
repository: springio/concourse-release-scripts
tag: '0.3.4'
username: ((docker-hub-username))
password: ((docker-hub-password))
inputs:
- name: git-repo
- name: artifactory-repo
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.3.27
version=5.3.29
org.gradle.jvmargs=-Xmx2048m
org.gradle.caching=true
org.gradle.parallel=true
+3 -4
View File
@@ -3,7 +3,7 @@ configurations {
}
dependencies {
asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.5.0")
asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.6.1")
}
repositories {
@@ -73,9 +73,8 @@ pluginManager.withPlugin("kotlin") {
}
task downloadResources(type: Download) {
def version = "0.2.5"
src "https://repo.spring.io/release/io/spring/docresources/" +
"spring-doc-resources/$version/spring-doc-resources-${version}.zip"
src "https://repo.spring.io/artifactory/snapshot/io/spring/docresources/" +
"spring-doc-resources/0.2.6-SNAPSHOT/spring-doc-resources-0.2.6-20210308.231804-2.zip"
dest project.file("$buildDir/docs/spring-doc-resources.zip")
onlyIfModified true
useETag "all"
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,6 +38,14 @@ import org.springframework.util.StringUtils;
* for an advice method from the pointcut expression, returning, and throwing clauses.
* If an unambiguous interpretation is not available, it returns {@code null}.
*
* <h3>Algorithm Summary</h3>
* <p>If an unambiguous binding can be deduced, then it is.
* If the advice requirements cannot possibly be satisfied, then {@code null}
* is returned. By setting the {@link #setRaiseExceptions(boolean) raiseExceptions}
* property to {@code true}, descriptive exceptions will be thrown instead of
* returning {@code null} in the case that the parameter names cannot be discovered.
*
* <h3>Algorithm Details</h3>
* <p>This class interprets arguments in the following way:
* <ol>
* <li>If the first parameter of the method is of type {@link JoinPoint}
@@ -65,15 +73,15 @@ import org.springframework.util.StringUtils;
* zero we proceed to the next stage. If {@code a} &gt; 1 then an
* {@code AmbiguousBindingException} is raised. If {@code a} == 1,
* and there are no unbound arguments of type {@code Annotation+},
* then an {@code IllegalArgumentException} is raised. if there is
* then an {@code IllegalArgumentException} is raised. If there is
* exactly one such argument, then the corresponding parameter name is
* assigned the value from the pointcut expression.</li>
* <li>If a returningName has been set, and there are no unbound arguments
* <li>If a {@code returningName} has been set, and there are no unbound arguments
* then an {@code IllegalArgumentException} is raised. If there is
* more than one unbound argument then an
* {@code AmbiguousBindingException} is raised. If there is exactly
* one unbound argument then the corresponding parameter name is assigned
* the value &lt;returningName&gt;.</li>
* the value of the {@code returningName}.</li>
* <li>If there remain unbound arguments, then the pointcut expression is
* examined once more for {@code this}, {@code target}, and
* {@code args} pointcut expressions used in the binding form (binding
@@ -99,20 +107,12 @@ import org.springframework.util.StringUtils;
* <p>The behavior on raising an {@code IllegalArgumentException} or
* {@code AmbiguousBindingException} is configurable to allow this discoverer
* to be used as part of a chain-of-responsibility. By default the condition will
* be logged and the {@code getParameterNames(..)} method will simply return
* be logged and the {@link #getParameterNames(Method)} method will simply return
* {@code null}. If the {@link #setRaiseExceptions(boolean) raiseExceptions}
* property is set to {@code true}, the conditions will be thrown as
* {@code IllegalArgumentException} and {@code AmbiguousBindingException},
* respectively.
*
* <p>Was that perfectly clear? ;)
*
* <p>Short version: If an unambiguous binding can be deduced, then it is.
* If the advice requirements cannot possibly be satisfied, then {@code null}
* is returned. By setting the {@link #setRaiseExceptions(boolean) raiseExceptions}
* property to {@code true}, descriptive exceptions will be thrown instead of
* returning {@code null} in the case that the parameter names cannot be discovered.
*
* @author Adrian Colyer
* @author Juergen Hoeller
* @since 2.0
@@ -158,7 +158,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
/** The pointcut expression associated with the advice, as a simple String. */
@Nullable
private String pointcutExpression;
private final String pointcutExpression;
private boolean raiseExceptions;
@@ -197,7 +197,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
/**
* If {@code afterReturning} advice binds the return value, the
* returning variable name must be specified.
* {@code returning} variable name must be specified.
* @param returningName the name of the returning variable
*/
public void setReturningName(@Nullable String returningName) {
@@ -206,18 +206,17 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
/**
* If {@code afterThrowing} advice binds the thrown value, the
* throwing variable name must be specified.
* {@code throwing} variable name must be specified.
* @param throwingName the name of the throwing variable
*/
public void setThrowingName(@Nullable String throwingName) {
this.throwingName = throwingName;
}
/**
* Deduce the parameter names for an advice method.
* <p>See the {@link AspectJAdviceParameterNameDiscoverer class level javadoc}
* for this class for details of the algorithm used.
* <p>See the {@link AspectJAdviceParameterNameDiscoverer class-level javadoc}
* for this class for details on the algorithm used.
* @param method the target {@link Method}
* @return the parameter names
*/
@@ -316,13 +315,13 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
private void bindParameterName(int index, String name) {
private void bindParameterName(int index, @Nullable String name) {
this.parameterNameBindings[index] = name;
this.numberOfRemainingUnboundArguments--;
}
/**
* If the first parameter is of type JoinPoint or ProceedingJoinPoint,bind "thisJoinPoint" as
* If the first parameter is of type JoinPoint or ProceedingJoinPoint, bind "thisJoinPoint" as
* parameter name and return true, else return false.
*/
private boolean maybeBindThisJoinPoint() {
@@ -367,8 +366,8 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
if (throwableIndex == -1) {
throw new IllegalStateException("Binding of throwing parameter '" + this.throwingName
+ "' could not be completed as no available arguments are a subtype of Throwable");
throw new IllegalStateException("Binding of throwing parameter '" + this.throwingName +
"' could not be completed as no available arguments are a subtype of Throwable");
}
else {
bindParameterName(throwableIndex, this.throwingName);
@@ -400,7 +399,6 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
}
/**
* Parse the string pointcut expression looking for:
* &#64;this, &#64;target, &#64;args, &#64;within, &#64;withincode, &#64;annotation.
@@ -465,7 +463,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
}
/*
/**
* If the token starts meets Java identifier conventions, it's in.
*/
@Nullable
@@ -533,7 +531,6 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
}
}
if (varNames.size() > 1) {
throw new AmbiguousBindingException("Found " + varNames.size() +
" candidate this(), target() or args() variables but only one unbound argument slot");
@@ -609,7 +606,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
// else varNames.size must be 0 and we have nothing to bind.
}
/*
/**
* We've found the start of a binding pointcut at the given index into the
* token array. Now we need to extract the pointcut body and return it.
*/
@@ -709,7 +706,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
return false;
}
/*
/**
* Return {@code true} if the given argument type is a subclass
* of the given supertype.
*/
@@ -737,7 +734,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
return count;
}
/*
/**
* Find the argument index with the given type, and bind the given
* {@code varName} in that position.
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -521,6 +521,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
copy.copyFrom(this);
copy.targetSource = EmptyTargetSource.forClass(getTargetClass(), getTargetSource().isStatic());
copy.advisorChainFactory = this.advisorChainFactory;
copy.methodCache = this.methodCache;
copy.interfaces = new ArrayList<>(this.interfaces);
copy.advisors = new ArrayList<>(this.advisors);
return copy;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -283,8 +283,8 @@ class CglibAopProxy implements AopProxy, Serializable {
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
// Parameters used for optimization choices...
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean exposeProxy = this.advised.isExposeProxy();
boolean isStatic = this.advised.getTargetSource().isStatic();
// Choose an "aop" interceptor (used for AOP calls).
@@ -899,9 +899,9 @@ class CglibAopProxy implements AopProxy, Serializable {
// Proxy is not yet available, but that shouldn't matter.
List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
boolean haveAdvice = !chain.isEmpty();
boolean isFrozen = this.advised.isFrozen();
boolean exposeProxy = this.advised.isExposeProxy();
boolean isStatic = this.advised.getTargetSource().isStatic();
boolean isFrozen = this.advised.isFrozen();
if (haveAdvice || !isFrozen) {
// If exposing the proxy, then AOP_PROXY must be used.
if (exposeProxy) {
@@ -970,6 +970,9 @@ class CglibAopProxy implements AopProxy, Serializable {
if (this.advised.isExposeProxy() != otherAdvised.isExposeProxy()) {
return false;
}
if (this.advised.isOpaque() != otherAdvised.isOpaque()) {
return false;
}
if (this.advised.getTargetSource().isStatic() != otherAdvised.getTargetSource().isStatic()) {
return false;
}
@@ -1016,10 +1019,6 @@ class CglibAopProxy implements AopProxy, Serializable {
Advice advice = advisor.getAdvice();
hashCode = 13 * hashCode + advice.getClass().hashCode();
}
hashCode = 13 * hashCode + (this.advised.isFrozen() ? 1 : 0);
hashCode = 13 * hashCode + (this.advised.isExposeProxy() ? 1 : 0);
hashCode = 13 * hashCode + (this.advised.isOptimize() ? 1 : 0);
hashCode = 13 * hashCode + (this.advised.isOpaque() ? 1 : 0);
return hashCode;
}
}
@@ -123,12 +123,33 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
if (classLoader == null || classLoader.getParent() == null) {
// JDK bootstrap loader or platform loader suggested ->
// use higher-level loader which can see Spring infrastructure classes
classLoader = getClass().getClassLoader();
return Proxy.newProxyInstance(determineClassLoader(classLoader), this.proxiedInterfaces, this);
}
/**
* Determine whether the JDK bootstrap or platform loader has been suggested ->
* use higher-level loader which can see Spring infrastructure classes instead.
*/
private ClassLoader determineClassLoader(@Nullable ClassLoader classLoader) {
if (classLoader == null) {
// JDK bootstrap loader -> use spring-aop ClassLoader instead.
return getClass().getClassLoader();
}
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
if (classLoader.getParent() == null) {
// Potentially the JDK platform loader on JDK 9+
ClassLoader aopClassLoader = getClass().getClassLoader();
ClassLoader aopParent = aopClassLoader.getParent();
while (aopParent != null) {
if (classLoader == aopParent) {
// Suggested ClassLoader is ancestor of spring-aop ClassLoader
// -> use spring-aop ClassLoader itself instead.
return aopClassLoader;
}
aopParent = aopParent.getParent();
}
}
// Regular case: use suggested ClassLoader as-is.
return classLoader;
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Convenient superclass for
@@ -81,6 +82,11 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
return this.beanFactory;
}
private ConfigurableBeanFactory getConfigurableBeanFactory() {
Assert.state(this.beanFactory != null, "BeanFactory not set");
return this.beanFactory;
}
//---------------------------------------------------------------------
// Implementation of the TargetSourceCreator interface
@@ -104,7 +110,7 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
// We need to override just this bean definition, as it may reference other beans
// and we're happy to take the parent's definition for those.
// Always use prototype scope if demanded.
BeanDefinition bd = this.beanFactory.getMergedBeanDefinition(beanName);
BeanDefinition bd = getConfigurableBeanFactory().getMergedBeanDefinition(beanName);
GenericBeanDefinition bdCopy = new GenericBeanDefinition(bd);
if (isPrototypeBased()) {
bdCopy.setScope(BeanDefinition.SCOPE_PROTOTYPE);
@@ -126,7 +132,7 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator
protected DefaultListableBeanFactory getInternalBeanFactoryForBean(String beanName) {
synchronized (this.internalBeanFactories) {
return this.internalBeanFactories.computeIfAbsent(beanName,
name -> buildInternalBeanFactory(this.beanFactory));
name -> buildInternalBeanFactory(getConfigurableBeanFactory()));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.aop.TargetSource;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
@@ -57,15 +59,18 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
protected final Log logger = LogFactory.getLog(getClass());
/** Name of the target bean we will create on each invocation. */
@Nullable
private String targetBeanName;
/** Class of the target. */
@Nullable
private volatile Class<?> targetClass;
/**
* BeanFactory that owns this TargetSource. We need to hold onto this
* reference so that we can create new prototype instances as necessary.
*/
@Nullable
private BeanFactory beanFactory;
@@ -86,6 +91,7 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
* Return the name of the target bean in the factory.
*/
public String getTargetBeanName() {
Assert.state(this.targetBeanName != null, "Target bean name not set");
return this.targetBeanName;
}
@@ -115,11 +121,13 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
* Return the owning BeanFactory.
*/
public BeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null, "BeanFactory not set");
return this.beanFactory;
}
@Override
@Nullable
public Class<?> getTargetClass() {
Class<?> targetClass = this.targetClass;
if (targetClass != null) {
@@ -128,7 +136,7 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
synchronized (this) {
// Full check within synchronization, entering the BeanFactory interaction algorithm only once...
targetClass = this.targetClass;
if (targetClass == null && this.beanFactory != null) {
if (targetClass == null && this.beanFactory != null && this.targetBeanName != null) {
// Determine type of the target bean.
targetClass = this.beanFactory.getType(this.targetBeanName);
if (targetClass == null) {
@@ -182,18 +190,16 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
@Override
public int hashCode() {
int hashCode = getClass().hashCode();
hashCode = 13 * hashCode + ObjectUtils.nullSafeHashCode(this.beanFactory);
hashCode = 13 * hashCode + ObjectUtils.nullSafeHashCode(this.targetBeanName);
return hashCode;
return getClass().hashCode() * 13 + ObjectUtils.nullSafeHashCode(this.targetBeanName);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(getClass().getSimpleName());
sb.append(" for target bean '").append(this.targetBeanName).append('\'');
if (this.targetClass != null) {
sb.append(" of type [").append(this.targetClass.getName()).append(']');
Class<?> targetClass = this.targetClass;
if (targetClass != null) {
sb.append(" of type [").append(targetClass.getName()).append(']');
}
return sb.toString();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,6 +46,7 @@ public abstract class AbstractLazyCreationTargetSource implements TargetSource {
protected final Log logger = LogFactory.getLog(getClass());
/** The lazily initialized target object. */
@Nullable
private Object lazyTarget;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -70,6 +70,7 @@ public final class EmptyTargetSource implements TargetSource, Serializable {
// Instance implementation
//---------------------------------------------------------------------
@Nullable
private final Class<?> targetClass;
private final boolean isStatic;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -95,8 +95,7 @@ public class HotSwappableTargetSource implements TargetSource, Serializable {
/**
* Two HotSwappableTargetSources are equal if the current target
* objects are equal.
* Two HotSwappableTargetSources are equal if the current target objects are equal.
*/
@Override
public boolean equals(Object other) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -82,14 +82,8 @@ public class SingletonTargetSource implements TargetSource, Serializable {
*/
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof SingletonTargetSource)) {
return false;
}
SingletonTargetSource otherTargetSource = (SingletonTargetSource) other;
return this.target.equals(otherTargetSource.target);
return (this == other || (other instanceof SingletonTargetSource &&
this.target.equals(((SingletonTargetSource) other).target)));
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,7 +58,12 @@ public class ThreadLocalTargetSource extends AbstractPrototypeBasedTargetSource
* is meant to be per thread per instance of the ThreadLocalTargetSource class.
*/
private final ThreadLocal<Object> targetInThread =
new NamedThreadLocal<>("Thread-local instance of bean '" + getTargetBeanName() + "'");
new NamedThreadLocal<Object>("Thread-local instance of bean") {
@Override
public String toString() {
return super.toString() + " '" + getTargetBeanName() + "'";
}
};
/**
* Set of managed targets, enabling us to keep track of the targets we've created.
@@ -388,7 +388,8 @@ public class ProxyFactoryTests {
CharSequence target = "test";
ProxyFactory pf = new ProxyFactory(target);
ClassLoader cl = target.getClass().getClassLoader();
assertThat(((CharSequence) pf.getProxy(cl)).toString()).isEqualTo(target);
CharSequence proxy = (CharSequence) pf.getProxy(cl);
assertThat(proxy.toString()).isEqualTo(target);
}
@Test
@@ -397,7 +398,8 @@ public class ProxyFactoryTests {
ProxyFactory pf = new ProxyFactory(target);
pf.setProxyTargetClass(true);
ClassLoader cl = target.getClass().getClassLoader();
assertThat(((Date) pf.getProxy(cl)).getTime()).isEqualTo(target.getTime());
Date proxy = (Date) pf.getProxy(cl);
assertThat(proxy.getTime()).isEqualTo(target.getTime());
}
@Test
@@ -414,7 +416,8 @@ public class ProxyFactoryTests {
};
ProxyFactory pf = new ProxyFactory(target);
ClassLoader cl = Savepoint.class.getClassLoader();
assertThat(((Savepoint) pf.getProxy(cl)).getSavepointName()).isEqualTo("sp");
Savepoint proxy = (Savepoint) pf.getProxy(cl);
assertThat(proxy.getSavepointName()).isEqualTo("sp");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -136,7 +136,10 @@ public interface BeanFactory {
* <p>Translates aliases back to the corresponding canonical bean name.
* <p>Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to retrieve
* @return an instance of the bean
* @return an instance of the bean.
* Note that the return value will never be {@code null} but possibly a stub for
* {@code null} returned from a factory method, to be checked via {@code equals(null)}.
* Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies.
* @throws NoSuchBeanDefinitionException if there is no bean with the specified name
* @throws BeansException if the bean could not be obtained
*/
@@ -152,7 +155,11 @@ public interface BeanFactory {
* <p>Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to retrieve
* @param requiredType type the bean must match; can be an interface or superclass
* @return an instance of the bean
* @return an instance of the bean.
* Note that the return value will never be {@code null}. In case of a stub for
* {@code null} from a factory method having been resolved for the requested bean, a
* {@code BeanNotOfRequiredTypeException} against the NullBean stub will be raised.
* Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies.
* @throws NoSuchBeanDefinitionException if there is no such bean definition
* @throws BeanNotOfRequiredTypeException if the bean is not of the required type
* @throws BeansException if the bean could not be created
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -594,7 +594,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
* Resolve the specified cached method argument or field value.
*/
@Nullable
private Object resolvedCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) {
private Object resolveCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) {
if (cachedArgument instanceof DependencyDescriptor) {
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
Assert.state(this.beanFactory != null, "No BeanFactory available");
@@ -629,10 +629,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
Object value;
if (this.cached) {
try {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
value = resolveCachedArgument(beanName, this.cachedFieldValue);
}
catch (NoSuchBeanDefinitionException ex) {
// Unexpected removal of target bean for cached argument -> re-resolve
catch (BeansException ex) {
// Unexpected target bean mismatch for cached argument -> re-resolve
this.cached = false;
logger.debug("Failed to resolve cached argument", ex);
value = resolveFieldValue(field, bean, beanName);
}
}
@@ -661,11 +663,10 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
}
synchronized (this) {
if (!this.cached) {
Object cachedFieldValue = null;
if (value != null || this.required) {
cachedFieldValue = desc;
Object cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
if (value != null && autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
@@ -673,9 +674,13 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
desc, autowiredBeanName, field.getType());
}
}
this.cachedFieldValue = cachedFieldValue;
this.cached = true;
}
else {
this.cachedFieldValue = null;
// cached flag remains false
}
this.cachedFieldValue = cachedFieldValue;
this.cached = true;
}
}
return value;
@@ -709,10 +714,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
Object[] arguments;
if (this.cached) {
try {
arguments = resolveCachedArguments(beanName);
arguments = resolveCachedArguments(beanName, this.cachedMethodArguments);
}
catch (NoSuchBeanDefinitionException ex) {
// Unexpected removal of target bean for cached argument -> re-resolve
catch (BeansException ex) {
// Unexpected target bean mismatch for cached argument -> re-resolve
this.cached = false;
logger.debug("Failed to resolve cached argument", ex);
arguments = resolveMethodArguments(method, bean, beanName);
}
}
@@ -731,14 +738,13 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
}
@Nullable
private Object[] resolveCachedArguments(@Nullable String beanName) {
Object[] cachedMethodArguments = this.cachedMethodArguments;
private Object[] resolveCachedArguments(@Nullable String beanName, @Nullable Object[] cachedMethodArguments) {
if (cachedMethodArguments == null) {
return null;
}
Object[] arguments = new Object[cachedMethodArguments.length];
for (int i = 0; i < arguments.length; i++) {
arguments[i] = resolvedCachedArgument(beanName, cachedMethodArguments[i]);
arguments[i] = resolveCachedArgument(beanName, cachedMethodArguments[i]);
}
return arguments;
}
@@ -771,14 +777,14 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
synchronized (this) {
if (!this.cached) {
if (arguments != null) {
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, argumentCount);
registerDependentBeans(beanName, autowiredBeans);
if (autowiredBeans.size() == argumentCount) {
Iterator<String> it = autowiredBeans.iterator();
Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
String autowiredBeanName = it.next();
if (beanFactory.containsBean(autowiredBeanName) &&
if (arguments[i] != null && beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
descriptors[i], autowiredBeanName, paramTypes[i]);
@@ -786,11 +792,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
}
}
this.cachedMethodArguments = cachedMethodArguments;
this.cached = true;
}
else {
this.cachedMethodArguments = null;
// cached flag remains false
}
this.cached = true;
}
}
return arguments;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -961,7 +961,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
StartupStep smartInitialize = getApplicationStartup().start("spring.beans.smart-initialize")
.tag("beanName", beanName);
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -102,7 +102,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
private boolean singletonsCurrentlyInDestruction = false;
/** Disposable bean instances: bean name to disposable instance. */
private final Map<String, Object> disposableBeans = new LinkedHashMap<>();
private final Map<String, DisposableBean> disposableBeans = new LinkedHashMap<>();
/** Map between containing bean names: bean name to Set of bean names that the bean contains. */
private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
@@ -340,7 +340,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
* (within the entire factory).
* @param beanName the name of the bean
*/
public boolean isSingletonCurrentlyInCreation(String beanName) {
public boolean isSingletonCurrentlyInCreation(@Nullable String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
@@ -447,17 +447,17 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
}
String canonicalName = canonicalName(beanName);
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans == null) {
if (dependentBeans == null || dependentBeans.isEmpty()) {
return false;
}
if (dependentBeans.contains(dependentBeanName)) {
return true;
}
if (alreadySeen == null) {
alreadySeen = new HashSet<>();
}
alreadySeen.add(beanName);
for (String transitiveDependency : dependentBeans) {
if (alreadySeen == null) {
alreadySeen = new HashSet<>();
}
alreadySeen.add(beanName);
if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
return true;
}
@@ -554,7 +554,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
// Destroy the corresponding DisposableBean instance.
DisposableBean disposableBean;
synchronized (this.disposableBeans) {
disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
disposableBean = this.disposableBeans.remove(beanName);
}
destroyBean(beanName, disposableBean);
}
@@ -68,8 +68,8 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
private static final String SHUTDOWN_METHOD_NAME = "shutdown";
private static final Log logger = LogFactory.getLog(DisposableBeanAdapter.class);
private static final Log logger = LogFactory.getLog(DisposableBeanAdapter.class);
private final Object bean;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -506,23 +506,12 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
if (isExternallyManagedInitMethod(initMethod)) {
return true;
}
if (this.externallyManagedInitMethods != null) {
for (String candidate : this.externallyManagedInitMethods) {
int indexOfDot = candidate.lastIndexOf('.');
if (indexOfDot >= 0) {
String methodName = candidate.substring(indexOfDot + 1);
if (methodName.equals(initMethod)) {
return true;
}
}
}
}
return false;
return hasAnyExternallyManagedMethod(this.externallyManagedInitMethods, initMethod);
}
}
/**
* Return all externally managed initialization methods (as an immutable Set).
* Get all externally managed initialization methods (as an immutable Set).
* <p>See {@link #registerExternallyManagedInitMethod} for details
* regarding the format for the initialization methods in the returned set.
* @since 5.3.11
@@ -583,19 +572,23 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
if (isExternallyManagedDestroyMethod(destroyMethod)) {
return true;
}
if (this.externallyManagedDestroyMethods != null) {
for (String candidate : this.externallyManagedDestroyMethods) {
int indexOfDot = candidate.lastIndexOf('.');
if (indexOfDot >= 0) {
String methodName = candidate.substring(indexOfDot + 1);
if (methodName.equals(destroyMethod)) {
return true;
}
return hasAnyExternallyManagedMethod(this.externallyManagedDestroyMethods, destroyMethod);
}
}
private static boolean hasAnyExternallyManagedMethod(Set<String> candidates, String methodName) {
if (candidates != null) {
for (String candidate : candidates) {
int indexOfDot = candidate.lastIndexOf('.');
if (indexOfDot > 0) {
String candidateMethodName = candidate.substring(indexOfDot + 1);
if (candidateMethodName.equals(methodName)) {
return true;
}
}
}
return false;
}
return false;
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -124,15 +124,89 @@ public class AutowiredAnnotationBeanPostProcessorTests {
TestBean tb = new TestBean();
bf.registerSingleton("testBean", tb);
ResourceInjectionBean bean = (ResourceInjectionBean) bf.getBean("annotatedBean");
ResourceInjectionBean bean = bf.getBean("annotatedBean", ResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
bean = (ResourceInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", ResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
}
@Test
public void testResourceInjectionWithNullBean() {
RootBeanDefinition bd = new RootBeanDefinition(NonPublicResourceInjectionBean.class);
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd);
RootBeanDefinition tb = new RootBeanDefinition(NullFactoryMethods.class);
tb.setFactoryMethodName("createTestBean");
bf.registerBeanDefinition("testBean", tb);
@SuppressWarnings("rawtypes")
NonPublicResourceInjectionBean bean = bf.getBean("annotatedBean", NonPublicResourceInjectionBean.class);
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
bean = bf.getBean("annotatedBean", NonPublicResourceInjectionBean.class);
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
}
@Test
void resourceInjectionWithSometimesNullBean() {
RootBeanDefinition bd = new RootBeanDefinition(OptionalResourceInjectionBean.class);
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd);
RootBeanDefinition tb = new RootBeanDefinition(SometimesNullFactoryMethods.class);
tb.setFactoryMethodName("createTestBean");
tb.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("testBean", tb);
SometimesNullFactoryMethods.active = false;
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
SometimesNullFactoryMethods.active = true;
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNotNull();
assertThat(bean.getTestBean2()).isNotNull();
assertThat(bean.getTestBean3()).isNotNull();
SometimesNullFactoryMethods.active = false;
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
SometimesNullFactoryMethods.active = false;
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
SometimesNullFactoryMethods.active = true;
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNotNull();
assertThat(bean.getTestBean2()).isNotNull();
assertThat(bean.getTestBean3()).isNotNull();
SometimesNullFactoryMethods.active = true;
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNotNull();
assertThat(bean.getTestBean2()).isNotNull();
assertThat(bean.getTestBean3()).isNotNull();
SometimesNullFactoryMethods.active = false;
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
}
@Test
public void testExtendedResourceInjection() {
RootBeanDefinition bd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class);
@@ -143,7 +217,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
NestedTestBean ntb = new NestedTestBean();
bf.registerSingleton("nestedTestBean", ntb);
TypedExtendedResourceInjectionBean bean = (TypedExtendedResourceInjectionBean) bf.getBean("annotatedBean");
TypedExtendedResourceInjectionBean bean = bf.getBean("annotatedBean", TypedExtendedResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -151,7 +225,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
assertThat(bean.getBeanFactory()).isSameAs(bf);
bean = (TypedExtendedResourceInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", TypedExtendedResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -173,7 +247,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("nestedTestBean", ntb);
TestBean tb = bf.getBean("testBean", TestBean.class);
TypedExtendedResourceInjectionBean bean = (TypedExtendedResourceInjectionBean) bf.getBean("annotatedBean");
TypedExtendedResourceInjectionBean bean = bf.getBean("annotatedBean", TypedExtendedResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -200,7 +274,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
NestedTestBean ntb = new NestedTestBean();
bf.registerSingleton("nestedTestBean", ntb);
TypedExtendedResourceInjectionBean bean = (TypedExtendedResourceInjectionBean) bf.getBean("annotatedBean");
TypedExtendedResourceInjectionBean bean = bf.getBean("annotatedBean", TypedExtendedResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb2);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -218,7 +292,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
NestedTestBean ntb = new NestedTestBean();
bf.registerSingleton("nestedTestBean", ntb);
OverriddenExtendedResourceInjectionBean bean = (OverriddenExtendedResourceInjectionBean) bf.getBean("annotatedBean");
OverriddenExtendedResourceInjectionBean bean = bf.getBean("annotatedBean", OverriddenExtendedResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -238,7 +312,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
NestedTestBean ntb = new NestedTestBean();
bf.registerSingleton("nestedTestBean", ntb);
DefaultMethodResourceInjectionBean bean = (DefaultMethodResourceInjectionBean) bf.getBean("annotatedBean");
DefaultMethodResourceInjectionBean bean = bf.getBean("annotatedBean", DefaultMethodResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -261,7 +335,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
NestedTestBean ntb = new NestedTestBean();
bf.registerSingleton("nestedTestBean", ntb);
TypedExtendedResourceInjectionBean bean = (TypedExtendedResourceInjectionBean) bf.getBean("annotatedBean");
TypedExtendedResourceInjectionBean bean = bf.getBean("annotatedBean", TypedExtendedResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -282,7 +356,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
NestedTestBean ntb2 = new NestedTestBean();
bf.registerSingleton("nestedTestBean2", ntb2);
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
OptionalResourceInjectionBean bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -310,7 +384,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
NestedTestBean ntb2 = new NestedTestBean();
bf.registerSingleton("nestedTestBean2", ntb2);
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
OptionalResourceInjectionBean bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -325,7 +399,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.destroySingleton("testBean");
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
@@ -340,7 +414,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("testBean", tb);
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -367,7 +441,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
NestedTestBean ntb2 = new NestedTestBean();
bf.registerSingleton("nestedTestBean2", ntb2);
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
OptionalResourceInjectionBean bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean2()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean3()).isSameAs(bf.getBean("testBean"));
@@ -382,7 +456,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.removeBeanDefinition("testBean");
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
@@ -397,7 +471,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean2()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean3()).isSameAs(bf.getBean("testBean"));
@@ -426,8 +500,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("nestedTestBean2", ntb2);
// Two calls to verify that caching doesn't break re-creation.
OptionalCollectionResourceInjectionBean bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
OptionalCollectionResourceInjectionBean bean = bf.getBean("annotatedBean", OptionalCollectionResourceInjectionBean.class);
bean = bf.getBean("annotatedBean", OptionalCollectionResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -457,8 +531,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("nestedTestBean1", ntb1);
// Two calls to verify that caching doesn't break re-creation.
OptionalCollectionResourceInjectionBean bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
OptionalCollectionResourceInjectionBean bean = bf.getBean("annotatedBean", OptionalCollectionResourceInjectionBean.class);
bean = bf.getBean("annotatedBean", OptionalCollectionResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -478,7 +552,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
TestBean tb = new TestBean();
bf.registerSingleton("testBean", tb);
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
OptionalResourceInjectionBean bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -490,7 +564,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public void testOptionalResourceInjectionWithNoDependencies() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(OptionalResourceInjectionBean.class));
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
OptionalResourceInjectionBean bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
@@ -512,7 +586,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
ntb2.setOrder(1);
bf.registerSingleton("nestedTestBean2", ntb2);
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
OptionalResourceInjectionBean bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -538,7 +612,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
bf.registerSingleton("nestedTestBean2", ntb2);
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
OptionalResourceInjectionBean bean = bf.getBean("annotatedBean", OptionalResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -569,8 +643,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("nestedTestBean2", ntb2);
// Two calls to verify that caching doesn't break re-creation.
OptionalCollectionResourceInjectionBean bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
OptionalCollectionResourceInjectionBean bean = bf.getBean("annotatedBean", OptionalCollectionResourceInjectionBean.class);
bean = bf.getBean("annotatedBean", OptionalCollectionResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -602,8 +676,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("nestedTestBean2", ntb2);
// Two calls to verify that caching doesn't break re-creation.
OptionalCollectionResourceInjectionBean bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
OptionalCollectionResourceInjectionBean bean = bf.getBean("annotatedBean", OptionalCollectionResourceInjectionBean.class);
bean = bf.getBean("annotatedBean", OptionalCollectionResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -630,7 +704,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
NestedTestBean ntb = new NestedTestBean();
bf.registerSingleton("nestedTestBean", ntb);
ConstructorResourceInjectionBean bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
ConstructorResourceInjectionBean bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -638,7 +712,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
assertThat(bean.getBeanFactory()).isSameAs(bf);
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -657,7 +731,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
NestedTestBean ntb = new NestedTestBean();
bf.registerSingleton("nestedTestBean", ntb);
ConstructorResourceInjectionBean bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
ConstructorResourceInjectionBean bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -667,7 +741,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.destroySingleton("nestedTestBean");
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -677,7 +751,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("nestedTestBean", ntb);
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -695,7 +769,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("testBean", tb);
bf.registerBeanDefinition("nestedTestBean", new RootBeanDefinition(NestedTestBean.class));
ConstructorResourceInjectionBean bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
ConstructorResourceInjectionBean bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -705,7 +779,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.removeBeanDefinition("nestedTestBean");
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -715,7 +789,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("nestedTestBean", new RootBeanDefinition(NestedTestBean.class));
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -734,7 +808,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("nestedTestBean", new RootBeanDefinition(NullNestedTestBeanFactoryBean.class));
bf.registerSingleton("nestedTestBean2", new NestedTestBean());
ConstructorResourceInjectionBean bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
ConstructorResourceInjectionBean bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -742,7 +816,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getNestedTestBean()).isNull();
assertThat(bean.getBeanFactory()).isSameAs(bf);
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
@@ -764,7 +838,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("nestedTestBean", ntb);
bf.registerSingleton("nestedTestBean2", new NestedTestBean());
ConstructorResourceInjectionBean bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
ConstructorResourceInjectionBean bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
@@ -772,7 +846,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getNestedTestBean()).isNull();
assertThat(bean.getBeanFactory()).isSameAs(bf);
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", ConstructorResourceInjectionBean.class);
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
@@ -791,7 +865,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
NestedTestBean ntb2 = new NestedTestBean();
bf.registerSingleton("nestedTestBean2", ntb2);
ConstructorsResourceInjectionBean bean = (ConstructorsResourceInjectionBean) bf.getBean("annotatedBean");
ConstructorsResourceInjectionBean bean = bf.getBean("annotatedBean", ConstructorsResourceInjectionBean.class);
assertThat(bean.getTestBean3()).isNull();
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBeans().length).isEqualTo(2);
@@ -802,8 +876,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
@Test
public void testConstructorResourceInjectionWithNoCandidatesAndNoFallback() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ConstructorWithoutFallbackBean.class));
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() ->
bf.getBean("annotatedBean"))
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(() -> bf.getBean("annotatedBean"))
.satisfies(methodParameterDeclaredOn(ConstructorWithoutFallbackBean.class));
}
@@ -817,7 +891,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
NestedTestBean ntb2 = new NestedTestBean();
bf.registerSingleton("nestedTestBean2", ntb2);
ConstructorsCollectionResourceInjectionBean bean = (ConstructorsCollectionResourceInjectionBean) bf.getBean("annotatedBean");
ConstructorsCollectionResourceInjectionBean bean = bf.getBean("annotatedBean", ConstructorsCollectionResourceInjectionBean.class);
assertThat(bean.getTestBean3()).isNull();
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBeans().size()).isEqualTo(1);
@@ -839,7 +913,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
NestedTestBean ntb2 = new NestedTestBean();
bf.registerSingleton("nestedTestBean2", ntb2);
ConstructorsCollectionResourceInjectionBean bean = (ConstructorsCollectionResourceInjectionBean) bf.getBean("annotatedBean");
ConstructorsCollectionResourceInjectionBean bean = bf.getBean("annotatedBean", ConstructorsCollectionResourceInjectionBean.class);
assertThat(bean.getTestBean3()).isNull();
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBeans().size()).isEqualTo(2);
@@ -857,7 +931,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
bf.registerSingleton("nestedTestBean2", ntb2);
ConstructorsResourceInjectionBean bean = (ConstructorsResourceInjectionBean) bf.getBean("annotatedBean");
ConstructorsResourceInjectionBean bean = bf.getBean("annotatedBean", ConstructorsResourceInjectionBean.class);
assertThat(bean.getTestBean3()).isNull();
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBeans().length).isEqualTo(2);
@@ -875,7 +949,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
bf.registerSingleton("nestedTestBean2", ntb2);
ConstructorsCollectionResourceInjectionBean bean = (ConstructorsCollectionResourceInjectionBean) bf.getBean("annotatedBean");
ConstructorsCollectionResourceInjectionBean bean = bf.getBean("annotatedBean", ConstructorsCollectionResourceInjectionBean.class);
assertThat(bean.getTestBean3()).isNull();
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBeans().size()).isEqualTo(2);
@@ -893,7 +967,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
bf.registerSingleton("nestedTestBean2", ntb2);
SingleConstructorVarargBean bean = (SingleConstructorVarargBean) bf.getBean("annotatedBean");
SingleConstructorVarargBean bean = bf.getBean("annotatedBean", SingleConstructorVarargBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getNestedTestBeans().size()).isEqualTo(2);
assertThat(bean.getNestedTestBeans().get(0)).isSameAs(ntb2);
@@ -906,7 +980,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
TestBean tb = new TestBean();
bf.registerSingleton("testBean", tb);
SingleConstructorVarargBean bean = (SingleConstructorVarargBean) bf.getBean("annotatedBean");
SingleConstructorVarargBean bean = bf.getBean("annotatedBean", SingleConstructorVarargBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getNestedTestBeans()).isNotNull();
assertThat(bean.getNestedTestBeans().isEmpty()).isTrue();
@@ -922,7 +996,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
bf.registerSingleton("nestedTestBean2", ntb2);
SingleConstructorRequiredCollectionBean bean = (SingleConstructorRequiredCollectionBean) bf.getBean("annotatedBean");
SingleConstructorRequiredCollectionBean bean = bf.getBean("annotatedBean", SingleConstructorRequiredCollectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getNestedTestBeans().size()).isEqualTo(2);
assertThat(bean.getNestedTestBeans().get(0)).isSameAs(ntb2);
@@ -935,7 +1009,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
TestBean tb = new TestBean();
bf.registerSingleton("testBean", tb);
SingleConstructorRequiredCollectionBean bean = (SingleConstructorRequiredCollectionBean) bf.getBean("annotatedBean");
SingleConstructorRequiredCollectionBean bean = bf.getBean("annotatedBean", SingleConstructorRequiredCollectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getNestedTestBeans()).isNotNull();
assertThat(bean.getNestedTestBeans().isEmpty()).isTrue();
@@ -951,7 +1025,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
bf.registerSingleton("nestedTestBean2", ntb2);
SingleConstructorOptionalCollectionBean bean = (SingleConstructorOptionalCollectionBean) bf.getBean("annotatedBean");
SingleConstructorOptionalCollectionBean bean = bf.getBean("annotatedBean", SingleConstructorOptionalCollectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getNestedTestBeans().size()).isEqualTo(2);
assertThat(bean.getNestedTestBeans().get(0)).isSameAs(ntb2);
@@ -964,7 +1038,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
TestBean tb = new TestBean();
bf.registerSingleton("testBean", tb);
SingleConstructorOptionalCollectionBean bean = (SingleConstructorOptionalCollectionBean) bf.getBean("annotatedBean");
SingleConstructorOptionalCollectionBean bean = bf.getBean("annotatedBean", SingleConstructorOptionalCollectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getNestedTestBeans()).isNull();
}
@@ -972,8 +1046,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
@Test
public void testSingleConstructorInjectionWithMissingDependency() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorOptionalCollectionBean.class));
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() ->
bf.getBean("annotatedBean"));
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(() -> bf.getBean("annotatedBean"));
}
@Test
@@ -982,8 +1056,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
RootBeanDefinition tb = new RootBeanDefinition(NullFactoryMethods.class);
tb.setFactoryMethodName("createTestBean");
bf.registerBeanDefinition("testBean", tb);
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() ->
bf.getBean("annotatedBean"));
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(() -> bf.getBean("annotatedBean"));
}
@Test
@@ -992,7 +1066,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
TestBean tb = new TestBean();
bf.registerSingleton("testBean", tb);
ConstructorsResourceInjectionBean bean = (ConstructorsResourceInjectionBean) bf.getBean("annotatedBean");
ConstructorsResourceInjectionBean bean = bf.getBean("annotatedBean", ConstructorsResourceInjectionBean.class);
assertThat(bean.getTestBean3()).isSameAs(tb);
assertThat(bean.getTestBean4()).isNull();
}
@@ -1001,7 +1075,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public void testConstructorResourceInjectionWithMultipleCandidatesAndDefaultFallback() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ConstructorsResourceInjectionBean.class));
ConstructorsResourceInjectionBean bean = (ConstructorsResourceInjectionBean) bf.getBean("annotatedBean");
ConstructorsResourceInjectionBean bean = bf.getBean("annotatedBean", ConstructorsResourceInjectionBean.class);
assertThat(bean.getTestBean3()).isNull();
assertThat(bean.getTestBean4()).isNull();
}
@@ -1017,12 +1091,12 @@ public class AutowiredAnnotationBeanPostProcessorTests {
tb2.setFactoryMethodName("createTestBean");
bf.registerBeanDefinition("testBean2", tb2);
MapConstructorInjectionBean bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean");
MapConstructorInjectionBean bean = bf.getBean("annotatedBean", MapConstructorInjectionBean.class);
assertThat(bean.getTestBeanMap().size()).isEqualTo(1);
assertThat(bean.getTestBeanMap().get("testBean1")).isSameAs(tb1);
assertThat(bean.getTestBeanMap().get("testBean2")).isNull();
bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", MapConstructorInjectionBean.class);
assertThat(bean.getTestBeanMap().size()).isEqualTo(1);
assertThat(bean.getTestBeanMap().get("testBean1")).isSameAs(tb1);
assertThat(bean.getTestBeanMap().get("testBean2")).isNull();
@@ -1038,14 +1112,14 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("testBean1", tb1);
bf.registerSingleton("testBean2", tb2);
MapFieldInjectionBean bean = (MapFieldInjectionBean) bf.getBean("annotatedBean");
MapFieldInjectionBean bean = bf.getBean("annotatedBean", MapFieldInjectionBean.class);
assertThat(bean.getTestBeanMap().size()).isEqualTo(2);
assertThat(bean.getTestBeanMap().keySet().contains("testBean1")).isTrue();
assertThat(bean.getTestBeanMap().keySet().contains("testBean2")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb1)).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb2)).isTrue();
bean = (MapFieldInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", MapFieldInjectionBean.class);
assertThat(bean.getTestBeanMap().size()).isEqualTo(2);
assertThat(bean.getTestBeanMap().keySet().contains("testBean1")).isTrue();
assertThat(bean.getTestBeanMap().keySet().contains("testBean2")).isTrue();
@@ -1061,13 +1135,13 @@ public class AutowiredAnnotationBeanPostProcessorTests {
TestBean tb = new TestBean();
bf.registerSingleton("testBean", tb);
MapMethodInjectionBean bean = (MapMethodInjectionBean) bf.getBean("annotatedBean");
MapMethodInjectionBean bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
assertThat(bean.getTestBeanMap().size()).isEqualTo(1);
assertThat(bean.getTestBeanMap().keySet().contains("testBean")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb)).isTrue();
assertThat(bean.getTestBean()).isSameAs(tb);
bean = (MapMethodInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
assertThat(bean.getTestBeanMap().size()).isEqualTo(1);
assertThat(bean.getTestBeanMap().keySet().contains("testBean")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb)).isTrue();
@@ -1079,9 +1153,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(MapMethodInjectionBean.class));
bf.registerBeanDefinition("testBean1", new RootBeanDefinition(TestBean.class));
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
assertThatExceptionOfType(UnsatisfiedDependencyException.class).as("should have failed, more than one bean of type").isThrownBy(() ->
bf.getBean("annotatedBean"))
.satisfies(methodParameterDeclaredOn(MapMethodInjectionBean.class));
assertThatExceptionOfType(UnsatisfiedDependencyException.class).as("should have failed, more than one bean of type")
.isThrownBy(() -> bf.getBean("annotatedBean"))
.satisfies(methodParameterDeclaredOn(MapMethodInjectionBean.class));
}
@Test
@@ -1092,8 +1166,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
rbd2.setAutowireCandidate(false);
bf.registerBeanDefinition("testBean2", rbd2);
MapMethodInjectionBean bean = (MapMethodInjectionBean) bf.getBean("annotatedBean");
TestBean tb = (TestBean) bf.getBean("testBean1");
MapMethodInjectionBean bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
TestBean tb = bf.getBean("testBean1", TestBean.class);
assertThat(bean.getTestBeanMap().size()).isEqualTo(1);
assertThat(bean.getTestBeanMap().keySet().contains("testBean1")).isTrue();
assertThat(bean.getTestBeanMap().values().contains(tb)).isTrue();
@@ -1104,7 +1178,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public void testMethodInjectionWithMapAndNoMatches() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(MapMethodInjectionBean.class));
MapMethodInjectionBean bean = (MapMethodInjectionBean) bf.getBean("annotatedBean");
MapMethodInjectionBean bean = bf.getBean("annotatedBean", MapMethodInjectionBean.class);
assertThat(bean.getTestBeanMap()).isNull();
assertThat(bean.getTestBean()).isNull();
}
@@ -1120,9 +1194,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("testBeans", tbm);
bf.registerSingleton("otherMap", new Properties());
MapConstructorInjectionBean bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean");
MapConstructorInjectionBean bean = bf.getBean("annotatedBean", MapConstructorInjectionBean.class);
assertThat(bean.getTestBeanMap()).isSameAs(tbm);
bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", MapConstructorInjectionBean.class);
assertThat(bean.getTestBeanMap()).isSameAs(tbm);
}
@@ -1136,9 +1210,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("myTestBeanMap", tbm);
bf.registerSingleton("otherMap", new HashMap<>());
MapConstructorInjectionBean bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean");
MapConstructorInjectionBean bean = bf.getBean("annotatedBean", MapConstructorInjectionBean.class);
assertThat(bean.getTestBeanMap()).isSameAs(bf.getBean("myTestBeanMap"));
bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", MapConstructorInjectionBean.class);
assertThat(bean.getTestBeanMap()).isSameAs(bf.getBean("myTestBeanMap"));
}
@@ -1153,9 +1227,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("testBean1", new TestBean());
bf.registerSingleton("testBean2", new TestBean());
CustomMapConstructorInjectionBean bean = (CustomMapConstructorInjectionBean) bf.getBean("annotatedBean");
CustomMapConstructorInjectionBean bean = bf.getBean("annotatedBean", CustomMapConstructorInjectionBean.class);
assertThat(bean.getTestBeanMap()).isSameAs(bf.getBean("myTestBeanMap"));
bean = (CustomMapConstructorInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", CustomMapConstructorInjectionBean.class);
assertThat(bean.getTestBeanMap()).isSameAs(bf.getBean("myTestBeanMap"));
}
@@ -1166,9 +1240,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("annotatedBean", bd);
bf.registerBeanDefinition("myTestBeanMap", new RootBeanDefinition(HashMap.class));
QualifiedMapConstructorInjectionBean bean = (QualifiedMapConstructorInjectionBean) bf.getBean("annotatedBean");
QualifiedMapConstructorInjectionBean bean = bf.getBean("annotatedBean", QualifiedMapConstructorInjectionBean.class);
assertThat(bean.getTestBeanMap()).isSameAs(bf.getBean("myTestBeanMap"));
bean = (QualifiedMapConstructorInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", QualifiedMapConstructorInjectionBean.class);
assertThat(bean.getTestBeanMap()).isSameAs(bf.getBean("myTestBeanMap"));
}
@@ -1183,9 +1257,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("testBeans", tbs);
bf.registerSingleton("otherSet", new HashSet<>());
SetConstructorInjectionBean bean = (SetConstructorInjectionBean) bf.getBean("annotatedBean");
SetConstructorInjectionBean bean = bf.getBean("annotatedBean", SetConstructorInjectionBean.class);
assertThat(bean.getTestBeanSet()).isSameAs(tbs);
bean = (SetConstructorInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", SetConstructorInjectionBean.class);
assertThat(bean.getTestBeanSet()).isSameAs(tbs);
}
@@ -1199,9 +1273,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("myTestBeanSet", tbs);
bf.registerSingleton("otherSet", new HashSet<>());
SetConstructorInjectionBean bean = (SetConstructorInjectionBean) bf.getBean("annotatedBean");
SetConstructorInjectionBean bean = bf.getBean("annotatedBean", SetConstructorInjectionBean.class);
assertThat(bean.getTestBeanSet()).isSameAs(bf.getBean("myTestBeanSet"));
bean = (SetConstructorInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", SetConstructorInjectionBean.class);
assertThat(bean.getTestBeanSet()).isSameAs(bf.getBean("myTestBeanSet"));
}
@@ -1214,9 +1288,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
tbs.setUniqueFactoryMethodName("testBeanSet");
bf.registerBeanDefinition("myTestBeanSet", tbs);
CustomSetConstructorInjectionBean bean = (CustomSetConstructorInjectionBean) bf.getBean("annotatedBean");
CustomSetConstructorInjectionBean bean = bf.getBean("annotatedBean", CustomSetConstructorInjectionBean.class);
assertThat(bean.getTestBeanSet()).isSameAs(bf.getBean("myTestBeanSet"));
bean = (CustomSetConstructorInjectionBean) bf.getBean("annotatedBean");
bean = bf.getBean("annotatedBean", CustomSetConstructorInjectionBean.class);
assertThat(bean.getTestBeanSet()).isSameAs(bf.getBean("myTestBeanSet"));
}
@@ -1224,7 +1298,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public void testSelfReference() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SelfInjectionBean.class));
SelfInjectionBean bean = (SelfInjectionBean) bf.getBean("annotatedBean");
SelfInjectionBean bean = bf.getBean("annotatedBean", SelfInjectionBean.class);
assertThat(bean.reference).isSameAs(bean);
assertThat(bean.referenceCollection).isNull();
}
@@ -1234,8 +1308,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SelfInjectionBean.class));
bf.registerBeanDefinition("annotatedBean2", new RootBeanDefinition(SelfInjectionBean.class));
SelfInjectionBean bean = (SelfInjectionBean) bf.getBean("annotatedBean");
SelfInjectionBean bean2 = (SelfInjectionBean) bf.getBean("annotatedBean2");
SelfInjectionBean bean = bf.getBean("annotatedBean", SelfInjectionBean.class);
SelfInjectionBean bean2 = bf.getBean("annotatedBean2", SelfInjectionBean.class);
assertThat(bean.reference).isSameAs(bean2);
assertThat(bean.referenceCollection.size()).isEqualTo(1);
assertThat(bean.referenceCollection.get(0)).isSameAs(bean2);
@@ -1245,7 +1319,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public void testSelfReferenceCollection() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SelfInjectionCollectionBean.class));
SelfInjectionCollectionBean bean = (SelfInjectionCollectionBean) bf.getBean("annotatedBean");
SelfInjectionCollectionBean bean = bf.getBean("annotatedBean", SelfInjectionCollectionBean.class);
assertThat(bean.reference).isSameAs(bean);
assertThat(bean.referenceCollection).isNull();
}
@@ -1255,8 +1329,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SelfInjectionCollectionBean.class));
bf.registerBeanDefinition("annotatedBean2", new RootBeanDefinition(SelfInjectionCollectionBean.class));
SelfInjectionCollectionBean bean = (SelfInjectionCollectionBean) bf.getBean("annotatedBean");
SelfInjectionCollectionBean bean2 = (SelfInjectionCollectionBean) bf.getBean("annotatedBean2");
SelfInjectionCollectionBean bean = bf.getBean("annotatedBean", SelfInjectionCollectionBean.class);
SelfInjectionCollectionBean bean2 = bf.getBean("annotatedBean2", SelfInjectionCollectionBean.class);
assertThat(bean.reference).isSameAs(bean2);
assertThat(bean2.referenceCollection.size()).isSameAs(1);
assertThat(bean.referenceCollection.get(0)).isSameAs(bean2);
@@ -1267,7 +1341,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryFieldInjectionBean.class));
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
ObjectFactoryFieldInjectionBean bean = (ObjectFactoryFieldInjectionBean) bf.getBean("annotatedBean");
ObjectFactoryFieldInjectionBean bean = bf.getBean("annotatedBean", ObjectFactoryFieldInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
}
@@ -1276,7 +1350,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryConstructorInjectionBean.class));
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
ObjectFactoryConstructorInjectionBean bean = (ObjectFactoryConstructorInjectionBean) bf.getBean("annotatedBean");
ObjectFactoryConstructorInjectionBean bean = bf.getBean("annotatedBean", ObjectFactoryConstructorInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
}
@@ -1287,9 +1361,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("annotatedBean", annotatedBeanDefinition);
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
ObjectFactoryFieldInjectionBean bean = (ObjectFactoryFieldInjectionBean) bf.getBean("annotatedBean");
ObjectFactoryFieldInjectionBean bean = bf.getBean("annotatedBean", ObjectFactoryFieldInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
ObjectFactoryFieldInjectionBean anotherBean = (ObjectFactoryFieldInjectionBean) bf.getBean("annotatedBean");
ObjectFactoryFieldInjectionBean anotherBean = bf.getBean("annotatedBean", ObjectFactoryFieldInjectionBean.class);
assertThat(bean).isNotSameAs(anotherBean);
assertThat(anotherBean.getTestBean()).isSameAs(bf.getBean("testBean"));
}
@@ -1302,7 +1376,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("dependencyBean", bd);
bf.registerBeanDefinition("dependencyBean2", new RootBeanDefinition(TestBean.class));
ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
ObjectFactoryQualifierInjectionBean bean = bf.getBean("annotatedBean", ObjectFactoryQualifierInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("dependencyBean"));
}
@@ -1314,7 +1388,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("dependencyBean", bd);
bf.registerBeanDefinition("dependencyBean2", new RootBeanDefinition(TestBean.class));
ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
ObjectFactoryQualifierInjectionBean bean = bf.getBean("annotatedBean", ObjectFactoryQualifierInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("dependencyBean"));
}
@@ -1324,7 +1398,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
bf.setSerializationId("test");
ObjectFactoryFieldInjectionBean bean = (ObjectFactoryFieldInjectionBean) bf.getBean("annotatedBean");
ObjectFactoryFieldInjectionBean bean = bf.getBean("annotatedBean", ObjectFactoryFieldInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
bean = SerializationTestUtils.serializeAndDeserialize(bean);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
@@ -1337,7 +1411,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
tbd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("testBean", tbd);
ObjectProviderInjectionBean bean = (ObjectProviderInjectionBean) bf.getBean("annotatedBean");
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
assertThat(bean.getTestBean()).isEqualTo(bf.getBean("testBean"));
assertThat(bean.getTestBean("myName")).isEqualTo(bf.getBean("testBean", "myName"));
assertThat(bean.getOptionalTestBean()).isEqualTo(bf.getBean("testBean"));
@@ -1366,7 +1440,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectProviderInjectionBean.class));
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
ObjectProviderInjectionBean bean = (ObjectProviderInjectionBean) bf.getBean("annotatedBean");
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getOptionalTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getOptionalTestBeanWithDefault()).isSameAs(bf.getBean("testBean"));
@@ -1393,7 +1467,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public void testObjectProviderInjectionWithTargetNotAvailable() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectProviderInjectionBean.class));
ObjectProviderInjectionBean bean = (ObjectProviderInjectionBean) bf.getBean("annotatedBean");
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(
bean::getTestBean);
assertThat(bean.getOptionalTestBean()).isNull();
@@ -1419,7 +1493,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("testBean1", new RootBeanDefinition(TestBean.class));
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
ObjectProviderInjectionBean bean = (ObjectProviderInjectionBean) bf.getBean("annotatedBean");
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(bean::getTestBean);
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(bean::getOptionalTestBean);
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(bean::consumeOptionalTestBean);
@@ -1456,7 +1530,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
tb2.setLazyInit(true);
bf.registerBeanDefinition("testBean2", tb2);
ObjectProviderInjectionBean bean = (ObjectProviderInjectionBean) bf.getBean("annotatedBean");
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean1"));
assertThat(bean.getOptionalTestBean()).isSameAs(bf.getBean("testBean1"));
assertThat(bean.consumeOptionalTestBean()).isSameAs(bf.getBean("testBean1"));
@@ -1494,7 +1568,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
tb2.setLazyInit(true);
bf.registerBeanDefinition("testBean2", tb2);
ObjectProviderInjectionBean bean = (ObjectProviderInjectionBean) bf.getBean("annotatedBean");
ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class);
List<?> testBeans = bean.sortedTestBeans();
assertThat(testBeans.size()).isEqualTo(2);
assertThat(testBeans.get(0)).isSameAs(bf.getBean("testBean2"));
@@ -1717,7 +1791,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
IntegerRepository ir = new IntegerRepository();
bf.registerSingleton("integerRepo", ir);
RepositoryFieldInjectionBean bean = (RepositoryFieldInjectionBean) bf.getBean("annotatedBean");
RepositoryFieldInjectionBean bean = bf.getBean("annotatedBean", RepositoryFieldInjectionBean.class);
assertThat(bean.string).isSameAs(sv);
assertThat(bean.integer).isSameAs(iv);
assertThat(bean.stringArray.length).isSameAs(1);
@@ -1762,7 +1836,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
IntegerRepository ir = new IntegerRepository();
bf.registerSingleton("integerRepo", ir);
RepositoryFieldInjectionBeanWithSubstitutedVariables bean = (RepositoryFieldInjectionBeanWithSubstitutedVariables) bf.getBean("annotatedBean");
RepositoryFieldInjectionBeanWithSubstitutedVariables bean =
bf.getBean("annotatedBean", RepositoryFieldInjectionBeanWithSubstitutedVariables.class);
assertThat(bean.string).isSameAs(sv);
assertThat(bean.integer).isSameAs(iv);
assertThat(bean.stringArray.length).isSameAs(1);
@@ -1803,7 +1878,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
IntegerRepository ir = new IntegerRepository();
bf.registerSingleton("integerRepo", ir);
RepositoryFieldInjectionBeanWithQualifiers bean = (RepositoryFieldInjectionBeanWithQualifiers) bf.getBean("annotatedBean");
RepositoryFieldInjectionBeanWithQualifiers bean =
bf.getBean("annotatedBean", RepositoryFieldInjectionBeanWithQualifiers.class);
assertThat(bean.stringRepository).isSameAs(sr);
assertThat(bean.integerRepository).isSameAs(ir);
assertThat(bean.stringRepositoryArray.length).isSameAs(1);
@@ -1840,7 +1916,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
rbd.setQualifiedElement(ReflectionUtils.findField(getClass(), "integerRepositoryQualifierProvider"));
bf.registerBeanDefinition("integerRepository", rbd); // Bean name not matching qualifier
RepositoryFieldInjectionBeanWithQualifiers bean = (RepositoryFieldInjectionBeanWithQualifiers) bf.getBean("annotatedBean");
RepositoryFieldInjectionBeanWithQualifiers bean =
bf.getBean("annotatedBean", RepositoryFieldInjectionBeanWithQualifiers.class);
Repository<?> sr = bf.getBean("stringRepo", Repository.class);
Repository<?> ir = bf.getBean("integerRepository", Repository.class);
assertThat(bean.stringRepository).isSameAs(sr);
@@ -1867,7 +1944,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerSingleton("repo", new StringRepository());
RepositoryFieldInjectionBeanWithSimpleMatch bean = (RepositoryFieldInjectionBeanWithSimpleMatch) bf.getBean("annotatedBean");
RepositoryFieldInjectionBeanWithSimpleMatch bean =
bf.getBean("annotatedBean", RepositoryFieldInjectionBeanWithSimpleMatch.class);
Repository<?> repo = bf.getBean("repo", Repository.class);
assertThat(bean.repository).isSameAs(repo);
assertThat(bean.stringRepository).isSameAs(repo);
@@ -1894,7 +1972,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("annotatedBean", bd);
bf.registerBeanDefinition("repoFactoryBean", new RootBeanDefinition(RepositoryFactoryBean.class));
RepositoryFactoryBeanInjectionBean bean = (RepositoryFactoryBeanInjectionBean) bf.getBean("annotatedBean");
RepositoryFactoryBeanInjectionBean bean = bf.getBean("annotatedBean", RepositoryFactoryBeanInjectionBean.class);
RepositoryFactoryBean<?> repoFactoryBean = bf.getBean("&repoFactoryBean", RepositoryFactoryBean.class);
assertThat(bean.repositoryFactoryBean).isSameAs(repoFactoryBean);
}
@@ -1906,7 +1984,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("annotatedBean", bd);
bf.registerSingleton("repoFactoryBean", new RepositoryFactoryBean<>());
RepositoryFactoryBeanInjectionBean bean = (RepositoryFactoryBeanInjectionBean) bf.getBean("annotatedBean");
RepositoryFactoryBeanInjectionBean bean = bf.getBean("annotatedBean", RepositoryFactoryBeanInjectionBean.class);
RepositoryFactoryBean<?> repoFactoryBean = bf.getBean("&repoFactoryBean", RepositoryFactoryBean.class);
assertThat(bean.repositoryFactoryBean).isSameAs(repoFactoryBean);
}
@@ -1925,7 +2003,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
rbd.getConstructorArgumentValues().addGenericArgumentValue(Repository.class);
bf.registerBeanDefinition("repo", rbd);
RepositoryFieldInjectionBeanWithSimpleMatch bean = (RepositoryFieldInjectionBeanWithSimpleMatch) bf.getBean("annotatedBean");
RepositoryFieldInjectionBeanWithSimpleMatch bean = bf.getBean("annotatedBean", RepositoryFieldInjectionBeanWithSimpleMatch.class);
Repository<?> repo = bf.getBean("repo", Repository.class);
assertThat(bean.repository).isSameAs(repo);
assertThat(bean.stringRepository).isSameAs(repo);
@@ -1956,7 +2034,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
rbd.getConstructorArgumentValues().addGenericArgumentValue(new TypedStringValue(Repository.class.getName()));
bf.registerBeanDefinition("repo", rbd);
RepositoryFieldInjectionBeanWithSimpleMatch bean = (RepositoryFieldInjectionBeanWithSimpleMatch) bf.getBean("annotatedBean");
RepositoryFieldInjectionBeanWithSimpleMatch bean = bf.getBean("annotatedBean", RepositoryFieldInjectionBeanWithSimpleMatch.class);
Repository<?> repo = bf.getBean("repo", Repository.class);
assertThat(bean.repository).isSameAs(repo);
assertThat(bean.stringRepository).isSameAs(repo);
@@ -1988,7 +2066,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
IntegerRepository ir = new IntegerRepository();
bf.registerSingleton("integerRepo", ir);
RepositoryMethodInjectionBean bean = (RepositoryMethodInjectionBean) bf.getBean("annotatedBean");
RepositoryMethodInjectionBean bean = bf.getBean("annotatedBean", RepositoryMethodInjectionBean.class);
assertThat(bean.string).isSameAs(sv);
assertThat(bean.integer).isSameAs(iv);
assertThat(bean.stringArray.length).isSameAs(1);
@@ -2033,7 +2111,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
IntegerRepository ir = new IntegerRepository();
bf.registerSingleton("integerRepo", ir);
RepositoryMethodInjectionBeanWithSubstitutedVariables bean = (RepositoryMethodInjectionBeanWithSubstitutedVariables) bf.getBean("annotatedBean");
RepositoryMethodInjectionBeanWithSubstitutedVariables bean =
bf.getBean("annotatedBean", RepositoryMethodInjectionBeanWithSubstitutedVariables.class);
assertThat(bean.string).isSameAs(sv);
assertThat(bean.integer).isSameAs(iv);
assertThat(bean.stringArray.length).isSameAs(1);
@@ -2074,7 +2153,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
IntegerRepository ir = new IntegerRepository();
bf.registerSingleton("integerRepo", ir);
RepositoryConstructorInjectionBean bean = (RepositoryConstructorInjectionBean) bf.getBean("annotatedBean");
RepositoryConstructorInjectionBean bean = bf.getBean("annotatedBean", RepositoryConstructorInjectionBean.class);
assertThat(bean.stringRepository).isSameAs(sr);
assertThat(bean.integerRepository).isSameAs(ir);
assertThat(bean.stringRepositoryArray.length).isSameAs(1);
@@ -2100,7 +2179,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
GenericRepository gr = new GenericRepository();
bf.registerSingleton("genericRepo", gr);
RepositoryConstructorInjectionBean bean = (RepositoryConstructorInjectionBean) bf.getBean("annotatedBean");
RepositoryConstructorInjectionBean bean = bf.getBean("annotatedBean", RepositoryConstructorInjectionBean.class);
assertThat(bean.stringRepository).isSameAs(gr);
assertThat(bean.integerRepository).isSameAs(gr);
assertThat(bean.stringRepositoryArray.length).isSameAs(1);
@@ -2125,7 +2204,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
SimpleRepository ngr = new SimpleRepository();
bf.registerSingleton("simpleRepo", ngr);
RepositoryConstructorInjectionBean bean = (RepositoryConstructorInjectionBean) bf.getBean("annotatedBean");
RepositoryConstructorInjectionBean bean = bf.getBean("annotatedBean", RepositoryConstructorInjectionBean.class);
assertThat(bean.stringRepository).isSameAs(ngr);
assertThat(bean.integerRepository).isSameAs(ngr);
assertThat(bean.stringRepositoryArray.length).isSameAs(1);
@@ -2153,7 +2232,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
GenericRepository gr = new GenericRepositorySubclass();
bf.registerSingleton("genericRepo", gr);
RepositoryConstructorInjectionBean bean = (RepositoryConstructorInjectionBean) bf.getBean("annotatedBean");
RepositoryConstructorInjectionBean bean = bf.getBean("annotatedBean", RepositoryConstructorInjectionBean.class);
assertThat(bean.stringRepository).isSameAs(sr);
assertThat(bean.integerRepository).isSameAs(gr);
assertThat(bean.stringRepositoryArray.length).isSameAs(1);
@@ -2180,7 +2259,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
SimpleRepository ngr = new SimpleRepositorySubclass();
bf.registerSingleton("simpleRepo", ngr);
RepositoryConstructorInjectionBean bean = (RepositoryConstructorInjectionBean) bf.getBean("annotatedBean");
RepositoryConstructorInjectionBean bean = bf.getBean("annotatedBean", RepositoryConstructorInjectionBean.class);
assertThat(bean.stringRepository).isSameAs(sr);
assertThat(bean.integerRepository).isSameAs(ngr);
assertThat(bean.stringRepositoryArray.length).isSameAs(1);
@@ -3876,6 +3955,20 @@ public class AutowiredAnnotationBeanPostProcessorTests {
}
public static class SometimesNullFactoryMethods {
public static boolean active = false;
public static TestBean createTestBean() {
return (active ? new TestBean() : null);
}
public static NestedTestBean createNestedTestBean() {
return (active ? new NestedTestBean() : null);
}
}
public static class ProvidedArgumentBean {
public ProvidedArgumentBean(String[] args) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,36 +28,32 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Chris Beams
* @since 04.07.2006
*/
public class DefaultSingletonBeanRegistryTests {
class DefaultSingletonBeanRegistryTests {
private final DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
@Test
public void testSingletons() {
DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
void singletons() {
TestBean tb = new TestBean();
beanRegistry.registerSingleton("tb", tb);
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
TestBean tb2 = (TestBean) beanRegistry.getSingleton("tb2", () -> new TestBean());
TestBean tb2 = (TestBean) beanRegistry.getSingleton("tb2", TestBean::new);
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
assertThat(beanRegistry.getSingletonCount()).isEqualTo(2);
String[] names = beanRegistry.getSingletonNames();
assertThat(names.length).isEqualTo(2);
assertThat(names[0]).isEqualTo("tb");
assertThat(names[1]).isEqualTo("tb2");
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb", "tb2");
beanRegistry.destroySingletons();
assertThat(beanRegistry.getSingletonCount()).isEqualTo(0);
assertThat(beanRegistry.getSingletonNames().length).isEqualTo(0);
assertThat(beanRegistry.getSingletonCount()).isZero();
assertThat(beanRegistry.getSingletonNames()).isEmpty();
}
@Test
public void testDisposableBean() {
DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
void disposableBean() {
DerivedTestBean tb = new DerivedTestBean();
beanRegistry.registerSingleton("tb", tb);
beanRegistry.registerDisposableBean("tb", tb);
@@ -65,21 +61,16 @@ public class DefaultSingletonBeanRegistryTests {
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
assertThat(beanRegistry.getSingletonCount()).isEqualTo(1);
String[] names = beanRegistry.getSingletonNames();
assertThat(names.length).isEqualTo(1);
assertThat(names[0]).isEqualTo("tb");
assertThat(beanRegistry.getSingletonNames()).containsExactly("tb");
assertThat(tb.wasDestroyed()).isFalse();
beanRegistry.destroySingletons();
assertThat(beanRegistry.getSingletonCount()).isEqualTo(0);
assertThat(beanRegistry.getSingletonNames().length).isEqualTo(0);
assertThat(tb.wasDestroyed()).isTrue();
assertThat(beanRegistry.getSingletonCount()).isZero();
assertThat(beanRegistry.getSingletonNames()).isEmpty();
}
@Test
public void testDependentRegistration() {
DefaultSingletonBeanRegistry beanRegistry = new DefaultSingletonBeanRegistry();
void dependentRegistration() {
beanRegistry.registerDependentBean("a", "b");
beanRegistry.registerDependentBean("b", "c");
beanRegistry.registerDependentBean("c", "b");
@@ -189,13 +189,11 @@ public class CaffeineCacheManager implements CacheManager {
@Override
@Nullable
public Cache getCache(String name) {
if (this.dynamic) {
Cache cache = this.cacheMap.get(name);
return (cache != null) ? cache : this.cacheMap.computeIfAbsent(name, this::createCaffeineCache);
}
else {
return this.cacheMap.get(name);
Cache cache = this.cacheMap.get(name);
if (cache == null && this.dynamic) {
cache = this.cacheMap.computeIfAbsent(name, this::createCaffeineCache);
}
return cache;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -166,13 +166,7 @@ public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderA
public Cache getCache(String name) {
Cache cache = this.cacheMap.get(name);
if (cache == null && this.dynamic) {
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
if (cache == null) {
cache = createConcurrentMapCache(name);
this.cacheMap.put(name, cache);
}
}
cache = this.cacheMap.computeIfAbsent(name, this::createConcurrentMapCache);
}
return cache;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,7 +47,7 @@ public class NameMatchCacheOperationSource implements CacheOperationSource, Seri
/** Keys are method names; values are TransactionAttributes. */
private Map<String, Collection<CacheOperation>> nameMap = new LinkedHashMap<>();
private final Map<String, Collection<CacheOperation>> nameMap = new LinkedHashMap<>();
/**
@@ -117,8 +117,8 @@ public class NameMatchCacheOperationSource implements CacheOperationSource, Seri
if (!(other instanceof NameMatchCacheOperationSource)) {
return false;
}
NameMatchCacheOperationSource otherTas = (NameMatchCacheOperationSource) other;
return ObjectUtils.nullSafeEquals(this.nameMap, otherTas.nameMap);
NameMatchCacheOperationSource otherCos = (NameMatchCacheOperationSource) other;
return ObjectUtils.nullSafeEquals(this.nameMap, otherCos.nameMap);
}
@Override
@@ -130,4 +130,5 @@ public class NameMatchCacheOperationSource implements CacheOperationSource, Seri
public String toString() {
return getClass().getName() + ": " + this.nameMap;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,8 @@ import org.springframework.instrument.classloading.LoadTimeWeaver;
/**
* Interface to be implemented by
* {@link org.springframework.context.annotation.Configuration @Configuration}
* classes annotated with {@link EnableLoadTimeWeaving @EnableLoadTimeWeaving} that wish to
* customize the {@link LoadTimeWeaver} instance to be used.
* classes annotated with {@link EnableLoadTimeWeaving @EnableLoadTimeWeaving}
* that wish to customize the {@link LoadTimeWeaver} instance to be used.
*
* <p>See {@link org.springframework.scheduling.annotation.EnableAsync @EnableAsync}
* for usage examples and information on how a default {@code LoadTimeWeaver}
@@ -36,9 +36,9 @@ import org.springframework.instrument.classloading.LoadTimeWeaver;
public interface LoadTimeWeavingConfigurer {
/**
* Create, configure and return the {@code LoadTimeWeaver} instance to be used. Note
* that it is unnecessary to annotate this method with {@code @Bean}, because the
* object returned will automatically be registered as a bean by
* Create, configure and return the {@code LoadTimeWeaver} instance to be used.
* Note that it is unnecessary to annotate this method with {@code @Bean}
* because the object returned will automatically be registered as a bean by
* {@link LoadTimeWeavingConfiguration#loadTimeWeaver()}
*/
LoadTimeWeaver getLoadTimeWeaver();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -274,7 +274,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
handleAsyncError(ex);
}
else if (event != null) {
publishEvent(event);
publishEvents(event);
}
});
}
@@ -367,7 +367,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
* Return the target bean instance to use.
*/
protected Object getTargetBean() {
Assert.notNull(this.applicationContext, "ApplicationContext must no be null");
Assert.notNull(this.applicationContext, "ApplicationContext must not be null");
return this.applicationContext.getBean(this.beanName);
}
@@ -468,6 +468,9 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
}
/**
* Inner class to avoid a hard dependency on the Reactive Streams API at runtime.
*/
private class ReactiveResultHandler {
public boolean subscribeToPublisher(Object result) {
@@ -481,6 +484,9 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
}
/**
* Reactive Streams Subscriber for publishing follow-up events.
*/
private class EventPublicationSubscriber implements Subscriber<Object> {
@Override
@@ -447,7 +447,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
@Override
public void setApplicationStartup(ApplicationStartup applicationStartup) {
Assert.notNull(applicationStartup, "applicationStartup should not be null");
Assert.notNull(applicationStartup, "ApplicationStartup must not be null");
this.applicationStartup = applicationStartup;
}
@@ -749,7 +749,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null &&
beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@ import java.security.ProtectionDomain;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.DecoratingClassLoader;
import org.springframework.core.OverridingClassLoader;
import org.springframework.core.SmartClassLoader;
@@ -45,15 +47,26 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
}
private static Method findLoadedClassMethod;
@Nullable
private static final Method findLoadedClassMethod;
static {
// Try to enable findLoadedClass optimization which allows us to selectively
// override classes that have not been loaded yet. If not accessible, we will
// always override requested classes, even when the classes have been loaded
// by the parent ClassLoader already and cannot be transformed anymore anyway.
Method method = null;
try {
findLoadedClassMethod = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
method = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
ReflectionUtils.makeAccessible(method);
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException("Invalid [java.lang.ClassLoader] class: no 'findLoadedClass' method defined!");
catch (Throwable ex) {
// Typically a JDK 9+ InaccessibleObjectException...
// Avoid through JVM startup with --add-opens=java.base/java.lang=ALL-UNNAMED
LogFactory.getLog(ContextTypeMatchClassLoader.class).debug(
"ClassLoader.findLoadedClass not accessible -> will always override requested class", ex);
}
findLoadedClassMethod = method;
}
@@ -96,13 +109,14 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
if (isExcluded(className) || ContextTypeMatchClassLoader.this.isExcluded(className)) {
return false;
}
ReflectionUtils.makeAccessible(findLoadedClassMethod);
ClassLoader parent = getParent();
while (parent != null) {
if (ReflectionUtils.invokeMethod(findLoadedClassMethod, parent, className) != null) {
return false;
if (findLoadedClassMethod != null) {
ClassLoader parent = getParent();
while (parent != null) {
if (ReflectionUtils.invokeMethod(findLoadedClassMethod, parent, className) != null) {
return false;
}
parent = parent.getParent();
}
parent = parent.getParent();
}
return true;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -64,9 +64,10 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
/**
* Specify the maximum time allotted in milliseconds for the shutdown of
* any phase (group of SmartLifecycle beans with the same 'phase' value).
* <p>The default value is 30 seconds.
* Specify the maximum time allotted in milliseconds for the shutdown of any
* phase (group of {@link SmartLifecycle} beans with the same 'phase' value).
* <p>The default value is 30000 milliseconds (30 seconds).
* @see SmartLifecycle#getPhase()
*/
public void setTimeoutPerShutdownPhase(long timeoutPerShutdownPhase) {
this.timeoutPerShutdownPhase = timeoutPerShutdownPhase;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -117,9 +117,12 @@ public @interface Scheduled {
* last invocation and the start of the next.
* <p>The time unit is milliseconds by default but can be overridden via
* {@link #timeUnit}.
* <p>This attribute variant supports Spring-style "${...}" placeholders
* as well as SpEL expressions.
* @return the delay as a String value &mdash; for example, a placeholder
* or a {@link java.time.Duration#parse java.time.Duration} compliant value
* @since 3.2.2
* @see #fixedDelay()
*/
String fixedDelayString() default "";
@@ -135,9 +138,12 @@ public @interface Scheduled {
* Execute the annotated method with a fixed period between invocations.
* <p>The time unit is milliseconds by default but can be overridden via
* {@link #timeUnit}.
* <p>This attribute variant supports Spring-style "${...}" placeholders
* as well as SpEL expressions.
* @return the period as a String value &mdash; for example, a placeholder
* or a {@link java.time.Duration#parse java.time.Duration} compliant value
* @since 3.2.2
* @see #fixedRate()
*/
String fixedRateString() default "";
@@ -156,9 +162,12 @@ public @interface Scheduled {
* {@link #fixedRate} or {@link #fixedDelay} task.
* <p>The time unit is milliseconds by default but can be overridden via
* {@link #timeUnit}.
* <p>This attribute variant supports Spring-style "${...}" placeholders
* as well as SpEL expressions.
* @return the initial delay as a String value &mdash; for example, a placeholder
* or a {@link java.time.Duration#parse java.time.Duration} compliant value
* @since 3.2.2
* @see #initialDelay()
*/
String initialDelayString() default "";
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -83,6 +83,9 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
private TaskExecutorAdapter adaptedExecutor;
@Nullable
private TaskDecorator taskDecorator;
/**
* Create a new ConcurrentTaskExecutor, using a single thread executor as default.
@@ -138,6 +141,7 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
* @since 4.3
*/
public final void setTaskDecorator(TaskDecorator taskDecorator) {
this.taskDecorator = taskDecorator;
this.adaptedExecutor.setTaskDecorator(taskDecorator);
}
@@ -174,11 +178,15 @@ public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, Sche
}
private static TaskExecutorAdapter getAdaptedExecutor(Executor concurrentExecutor) {
private TaskExecutorAdapter getAdaptedExecutor(Executor concurrentExecutor) {
if (managedExecutorServiceClass != null && managedExecutorServiceClass.isInstance(concurrentExecutor)) {
return new ManagedTaskExecutorAdapter(concurrentExecutor);
}
return new TaskExecutorAdapter(concurrentExecutor);
TaskExecutorAdapter adapter = new TaskExecutorAdapter(concurrentExecutor);
if (this.taskDecorator != null) {
adapter.setTaskDecorator(this.taskDecorator);
}
return adapter;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -100,7 +100,7 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
*/
public ConcurrentTaskScheduler() {
super();
this.scheduledExecutor = initScheduledExecutor(null);
initScheduledExecutor(null);
}
/**
@@ -115,7 +115,7 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
*/
public ConcurrentTaskScheduler(ScheduledExecutorService scheduledExecutor) {
super(scheduledExecutor);
this.scheduledExecutor = initScheduledExecutor(scheduledExecutor);
initScheduledExecutor(scheduledExecutor);
}
/**
@@ -131,11 +131,11 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
*/
public ConcurrentTaskScheduler(Executor concurrentExecutor, ScheduledExecutorService scheduledExecutor) {
super(concurrentExecutor);
this.scheduledExecutor = initScheduledExecutor(scheduledExecutor);
initScheduledExecutor(scheduledExecutor);
}
private ScheduledExecutorService initScheduledExecutor(@Nullable ScheduledExecutorService scheduledExecutor) {
private void initScheduledExecutor(@Nullable ScheduledExecutorService scheduledExecutor) {
if (scheduledExecutor != null) {
this.scheduledExecutor = scheduledExecutor;
this.enterpriseConcurrentScheduler = (managedScheduledExecutorServiceClass != null &&
@@ -145,7 +145,6 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
this.scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
this.enterpriseConcurrentScheduler = false;
}
return this.scheduledExecutor;
}
/**
@@ -207,9 +206,9 @@ public class ConcurrentTaskScheduler extends ConcurrentTaskExecutor implements T
@Override
public ScheduledFuture<?> schedule(Runnable task, Date startTime) {
long initialDelay = startTime.getTime() - this.clock.millis();
long delay = startTime.getTime() - this.clock.millis();
try {
return this.scheduledExecutor.schedule(decorateTask(task, false), initialDelay, TimeUnit.MILLISECONDS);
return this.scheduledExecutor.schedule(decorateTask(task, false), delay, TimeUnit.MILLISECONDS);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,10 @@ import org.springframework.lang.Nullable;
* Base class for setting up a {@link java.util.concurrent.ExecutorService}
* (typically a {@link java.util.concurrent.ThreadPoolExecutor} or
* {@link java.util.concurrent.ScheduledThreadPoolExecutor}).
* Defines common configuration settings and common lifecycle handling.
*
* <p>Defines common configuration settings and common lifecycle handling,
* inheriting thread customization options (name, priority, etc) from
* {@link org.springframework.util.CustomizableThreadCreator}.
*
* @author Juergen Hoeller
* @since 3.0
@@ -105,9 +108,9 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
/**
* Set whether to wait for scheduled tasks to complete on shutdown,
* not interrupting running tasks and executing all tasks in the queue.
* <p>Default is "false", shutting down immediately through interrupting
* ongoing tasks and clearing the queue. Switch this flag to "true" if you
* prefer fully completed tasks at the expense of a longer shutdown phase.
* <p>Default is {@code false}, shutting down immediately through interrupting
* ongoing tasks and clearing the queue. Switch this flag to {@code true} if
* you prefer fully completed tasks at the expense of a longer shutdown phase.
* <p>Note that Spring's container shutdown continues while ongoing tasks
* are being completed. If you want this executor to block and wait for the
* termination of tasks before the rest of the container continues to shut
@@ -199,8 +202,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
/**
* Calls {@code shutdown} when the BeanFactory destroys
* the task executor instance.
* Calls {@code shutdown} when the BeanFactory destroys the executor instance.
* @see #shutdown()
*/
@Override
@@ -209,9 +211,13 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
}
/**
* Perform a shutdown on the underlying ExecutorService.
* Perform a full shutdown on the underlying ExecutorService,
* according to the corresponding configuration settings.
* @see #setWaitForTasksToCompleteOnShutdown
* @see #setAwaitTerminationMillis
* @see java.util.concurrent.ExecutorService#shutdown()
* @see java.util.concurrent.ExecutorService#shutdownNow()
* @see java.util.concurrent.ExecutorService#awaitTermination
*/
public void shutdown() {
if (logger.isDebugEnabled()) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -78,8 +78,8 @@ class ReschedulingRunnable extends DelegatingErrorHandlingRunnable implements Sc
if (this.scheduledExecutionTime == null) {
return null;
}
long initialDelay = this.scheduledExecutionTime.getTime() - this.triggerContext.getClock().millis();
this.currentFuture = this.executor.schedule(this, initialDelay, TimeUnit.MILLISECONDS);
long delay = this.scheduledExecutionTime.getTime() - this.triggerContext.getClock().millis();
this.currentFuture = this.executor.schedule(this, delay, TimeUnit.MILLISECONDS);
return this;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -380,9 +380,9 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
@Override
public ScheduledFuture<?> schedule(Runnable task, Date startTime) {
ScheduledExecutorService executor = getScheduledExecutor();
long initialDelay = startTime.getTime() - this.clock.millis();
long delay = startTime.getTime() - this.clock.millis();
try {
return executor.schedule(errorHandlingTask(task, false), initialDelay, TimeUnit.MILLISECONDS);
return executor.schedule(errorHandlingTask(task, false), delay, TimeUnit.MILLISECONDS);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
@@ -124,8 +124,10 @@ public class FieldError extends ObjectError {
@Override
public String toString() {
// We would preferably use ObjectUtils.nullSafeConciseToString(rejectedValue) here but
// keep including the full nullSafeToString representation for backwards compatibility.
return "Field error in object '" + getObjectName() + "' on field '" + this.field +
"': rejected value [" + ObjectUtils.nullSafeConciseToString(this.rejectedValue) + "]; " +
"': rejected value [" + ObjectUtils.nullSafeToString(this.rejectedValue) + "]; " +
resolvableToString();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -108,7 +108,7 @@ open class BeanDefinitionDsl internal constructor (private val init: BeanDefinit
SINGLETON,
/**
* Scope constant for the standard singleton scope
* Scope constant for the standard prototype scope
* @see org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE
*/
PROTOTYPE
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@ import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.aop.testfixture.advice.CountingBeforeAdvice;
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
import org.springframework.beans.testfixture.beans.ITestBean;
@@ -35,6 +36,8 @@ import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -87,8 +90,7 @@ public class CglibProxyTests extends AbstractAopProxyTests implements Serializab
AdvisedSupport pc = new AdvisedSupport(ITestBean.class);
pc.addAdvice(new NopInterceptor());
AopProxy aop = createAopProxy(pc);
assertThatExceptionOfType(AopConfigException.class).isThrownBy(
aop::getProxy);
assertThatExceptionOfType(AopConfigException.class).isThrownBy(aop::getProxy);
}
@Test
@@ -136,8 +138,8 @@ public class CglibProxyTests extends AbstractAopProxyTests implements Serializab
Object proxy = aop.getProxy();
assertThat(AopUtils.isCglibProxy(proxy)).isTrue();
assertThat(proxy instanceof ITestBean).isTrue();
assertThat(proxy instanceof TestBean).isTrue();
assertThat(proxy).isInstanceOf(ITestBean.class);
assertThat(proxy).isInstanceOf(TestBean.class);
TestBean tb = (TestBean) proxy;
assertThat(tb.getAge()).isEqualTo(32);
@@ -304,6 +306,8 @@ public class CglibProxyTests extends AbstractAopProxyTests implements Serializab
CglibAopProxy cglib = new CglibAopProxy(as);
ITestBean proxy1 = (ITestBean) cglib.getProxy();
ITestBean proxy1a = (ITestBean) cglib.getProxy();
assertThat(proxy1a.getClass()).isSameAs(proxy1.getClass());
mockTargetSource.setTarget(proxy1);
as = new AdvisedSupport(new Class<?>[]{});
@@ -312,7 +316,40 @@ public class CglibProxyTests extends AbstractAopProxyTests implements Serializab
cglib = new CglibAopProxy(as);
ITestBean proxy2 = (ITestBean) cglib.getProxy();
assertThat(proxy2 instanceof Serializable).isTrue();
assertThat(proxy2).isInstanceOf(Serializable.class);
assertThat(proxy2.getClass()).isNotSameAs(proxy1.getClass());
ITestBean proxy2a = (ITestBean) cglib.getProxy();
assertThat(proxy2a).isInstanceOf(Serializable.class);
assertThat(proxy2a.getClass()).isSameAs(proxy2.getClass());
mockTargetSource.setTarget(proxy1);
as = new AdvisedSupport(new Class<?>[]{});
as.setTargetSource(mockTargetSource);
as.addAdvisor(new DefaultPointcutAdvisor(new AnnotationMatchingPointcut(Nullable.class), new NopInterceptor()));
cglib = new CglibAopProxy(as);
ITestBean proxy3 = (ITestBean) cglib.getProxy();
assertThat(proxy3).isInstanceOf(Serializable.class);
assertThat(proxy3.getClass()).isNotSameAs(proxy2.getClass());
ITestBean proxy3a = (ITestBean) cglib.getProxy();
assertThat(proxy3a).isInstanceOf(Serializable.class);
assertThat(proxy3a.getClass()).isSameAs(proxy3.getClass());
mockTargetSource.setTarget(proxy1);
as = new AdvisedSupport(new Class<?>[]{});
as.setTargetSource(mockTargetSource);
as.addAdvisor(new DefaultPointcutAdvisor(new AnnotationMatchingPointcut(NonNull.class), new NopInterceptor()));
cglib = new CglibAopProxy(as);
ITestBean proxy4 = (ITestBean) cglib.getProxy();
assertThat(proxy4).isInstanceOf(Serializable.class);
assertThat(proxy4.getClass()).isNotSameAs(proxy3.getClass());
ITestBean proxy4a = (ITestBean) cglib.getProxy();
assertThat(proxy4a).isInstanceOf(Serializable.class);
assertThat(proxy4a.getClass()).isSameAs(proxy4.getClass());
}
@Test
@@ -331,7 +368,7 @@ public class CglibProxyTests extends AbstractAopProxyTests implements Serializab
proxy.doTest();
}
catch (Exception ex) {
assertThat(ex instanceof ApplicationContextException).as("Invalid exception class").isTrue();
assertThat(ex).as("Invalid exception class").isInstanceOf(ApplicationContextException.class);
}
assertThat(proxy.isCatchInvoked()).as("Catch was not invoked").isTrue();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -207,10 +207,9 @@ public class ClassPathBeanDefinitionScannerTests {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
scanner.scan("org.springframework.context.annotation3");
assertThatIllegalStateException().isThrownBy(() ->
scanner.scan(BASE_PACKAGE))
.withMessageContaining("stubFooDao")
.withMessageContaining(StubFooDao.class.getName());
assertThatIllegalStateException().isThrownBy(() -> scanner.scan(BASE_PACKAGE))
.withMessageContaining("stubFooDao")
.withMessageContaining(StubFooDao.class.getName());
}
@Test
@@ -267,11 +266,10 @@ 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())
.withMessageContaining(NamedStubDao2.class.getName());
assertThatIllegalStateException().isThrownBy(() -> scanner.scan(BASE_PACKAGE))
.withMessageContaining("myNamedDao")
.withMessageContaining(NamedStubDao.class.getName())
.withMessageContaining(NamedStubDao2.class.getName());
}
@Test
@@ -79,7 +79,7 @@ class EnableLoadTimeWeavingTests {
@Configuration
@EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.DISABLED)
@EnableLoadTimeWeaving(aspectjWeaving = AspectJWeaving.DISABLED)
static class EnableLTWConfig_withAjWeavingDisabled implements LoadTimeWeavingConfigurer {
@Override
@@ -88,8 +88,9 @@ class EnableLoadTimeWeavingTests {
}
}
@Configuration
@EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.AUTODETECT)
@EnableLoadTimeWeaving(aspectjWeaving = AspectJWeaving.AUTODETECT)
static class EnableLTWConfig_withAjWeavingAutodetect implements LoadTimeWeavingConfigurer {
@Override
@@ -98,8 +99,9 @@ class EnableLoadTimeWeavingTests {
}
}
@Configuration
@EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.ENABLED)
@EnableLoadTimeWeaving(aspectjWeaving = AspectJWeaving.ENABLED)
static class EnableLTWConfig_withAjWeavingEnabled implements LoadTimeWeavingConfigurer {
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,6 +41,7 @@ class ImportTests {
private DefaultListableBeanFactory processConfigurationClasses(Class<?>... classes) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.setAllowBeanDefinitionOverriding(false);
for (Class<?> clazz : classes) {
beanFactory.registerBeanDefinition(clazz.getSimpleName(), new RootBeanDefinition(clazz));
}
@@ -56,9 +57,10 @@ class ImportTests {
for (Class<?> clazz : classes) {
beanFactory.getBean(clazz);
}
}
// ------------------------------------------------------------------------
@Test
void testProcessImportsWithAsm() {
int configClasses = 2;
@@ -158,6 +160,13 @@ class ImportTests {
assertBeanDefinitionCount(configClasses + beansInClasses, FirstLevel.class);
}
@Test
void testImportAnnotationWithThreeLevelRecursionAndDoubleImport() {
int configClasses = 5;
int beansInClasses = 5;
assertBeanDefinitionCount(configClasses + beansInClasses, FirstLevel.class, FirstLevelPlus.class);
}
// ------------------------------------------------------------------------
@Test
@@ -167,7 +176,6 @@ class ImportTests {
assertBeanDefinitionCount((configClasses + beansInClasses), WithMultipleArgumentsToImportAnnotation.class);
}
@Test
void testImportAnnotationWithMultipleArgumentsResultingInOverriddenBeanDefinition() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
@@ -245,6 +253,11 @@ class ImportTests {
}
}
@Configuration
@Import(ThirdLevel.class)
static class FirstLevelPlus {
}
@Configuration
@Import({ThirdLevel.class, InitBean.class})
static class SecondLevel {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ package org.springframework.context.event;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.List;
import org.junit.jupiter.api.Test;
@@ -46,6 +47,8 @@ import static org.mockito.Mockito.verify;
/**
* @author Stephane Nicoll
* @author Juergen Hoeller
* @author Simon Baslé
*/
public class ApplicationListenerMethodAdapterTests extends AbstractApplicationEventListenerTests {
@@ -80,16 +83,23 @@ public class ApplicationListenerMethodAdapterTests extends AbstractApplicationEv
supportsEventType(false, method, ResolvableType.forClassWithGenerics(GenericTestEvent.class, Long.class));
}
@Test
public void genericListenerWithUnresolvedGenerics() {
Method method = ReflectionUtils.findMethod(
SampleEvents.class, "handleGenericString", GenericTestEvent.class);
supportsEventType(true, method, ResolvableType.forClass(GenericTestEvent.class));
}
@Test
public void listenerWithPayloadAndGenericInformation() {
Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleString", String.class);
supportsEventType(true, method, createGenericEventType(String.class));
supportsEventType(true, method, createPayloadEventType(String.class));
}
@Test
public void listenerWithInvalidPayloadAndGenericInformation() {
Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleString", String.class);
supportsEventType(false, method, createGenericEventType(Integer.class));
supportsEventType(false, method, createPayloadEventType(Integer.class));
}
@Test
@@ -113,28 +123,28 @@ public class ApplicationListenerMethodAdapterTests extends AbstractApplicationEv
@Test
public void listenerWithAnnotationValue() {
Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleStringAnnotationValue");
supportsEventType(true, method, createGenericEventType(String.class));
supportsEventType(true, method, createPayloadEventType(String.class));
}
@Test
public void listenerWithAnnotationClasses() {
Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleStringAnnotationClasses");
supportsEventType(true, method, createGenericEventType(String.class));
supportsEventType(true, method, createPayloadEventType(String.class));
}
@Test
public void listenerWithAnnotationValueAndParameter() {
Method method = ReflectionUtils.findMethod(
SampleEvents.class, "handleStringAnnotationValueAndParameter", String.class);
supportsEventType(true, method, createGenericEventType(String.class));
supportsEventType(true, method, createPayloadEventType(String.class));
}
@Test
public void listenerWithSeveralTypes() {
Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleStringOrInteger");
supportsEventType(true, method, createGenericEventType(String.class));
supportsEventType(true, method, createGenericEventType(Integer.class));
supportsEventType(false, method, createGenericEventType(Double.class));
supportsEventType(true, method, createPayloadEventType(String.class));
supportsEventType(true, method, createPayloadEventType(Integer.class));
supportsEventType(false, method, createPayloadEventType(Double.class));
}
@Test
@@ -325,6 +335,88 @@ public class ApplicationListenerMethodAdapterTests extends AbstractApplicationEv
verify(this.context, times(2)).getBean("testBean");
}
@Test // gh-30399
void simplePayloadDoesNotSupportArbitraryGenericEventType() throws Exception {
Method method = SampleEvents.class.getDeclaredMethod("handleString", String.class);
ApplicationListenerMethodAdapter adapter = createTestInstance(method);
assertThat(adapter.supportsEventType(createPayloadEventType(ResolvableType.forClassWithGenerics(EntityWrapper.class, Integer.class))))
.as("handleString(String) with EntityWrapper<Integer>").isFalse();
assertThat(adapter.supportsEventType(createPayloadEventType(ResolvableType.forClass(EntityWrapper.class))))
.as("handleString(String) with EntityWrapper<?>").isFalse();
assertThat(adapter.supportsEventType(createPayloadEventType(ResolvableType.forClass(String.class))))
.as("handleString(String) with String").isTrue();
}
@Test // gh-30399
void genericPayloadDoesNotSupportArbitraryGenericEventType() throws Exception {
Method method = SampleEvents.class.getDeclaredMethod("handleGenericStringPayload", EntityWrapper.class);
ApplicationListenerMethodAdapter adapter = createTestInstance(method);
assertThat(adapter.supportsEventType(createPayloadEventType(ResolvableType.forClass(EntityWrapper.class))))
.as("handleGenericStringPayload(EntityWrapper<String>) with EntityWrapper<?>").isFalse();
assertThat(adapter.supportsEventType(createPayloadEventType(ResolvableType.forClassWithGenerics(EntityWrapper.class, Integer.class))))
.as("handleGenericStringPayload(EntityWrapper<String>) with EntityWrapper<Integer>").isFalse();
assertThat(adapter.supportsEventType(createPayloadEventType(ResolvableType.forClassWithGenerics(EntityWrapper.class, String.class))))
.as("handleGenericStringPayload(EntityWrapper<String>) with EntityWrapper<String>").isTrue();
}
@Test // gh-30399
void rawGenericPayloadDoesNotSupportArbitraryGenericEventType() throws Exception {
Method method = SampleEvents.class.getDeclaredMethod("handleGenericAnyPayload", EntityWrapper.class);
ApplicationListenerMethodAdapter adapter = createTestInstance(method);
assertThat(adapter.supportsEventType(createPayloadEventType(ResolvableType.forClass(EntityWrapper.class))))
.as("handleGenericAnyPayload(EntityWrapper<?>) with EntityWrapper<?>").isTrue();
assertThat(adapter.supportsEventType(createPayloadEventType(ResolvableType.forClassWithGenerics(EntityWrapper.class, Integer.class))))
.as("handleGenericAnyPayload(EntityWrapper<?>) with EntityWrapper<Integer>").isTrue();
assertThat(adapter.supportsEventType(createPayloadEventType(ResolvableType.forClassWithGenerics(EntityWrapper.class, String.class))))
.as("handleGenericAnyPayload(EntityWrapper<?>) with EntityWrapper<String>").isTrue();
assertThat(adapter.supportsEventType(createPayloadEventType(ResolvableType.forClass(List.class))))
.as("handleGenericAnyPayload(EntityWrapper<?>) with List<?>").isFalse();
assertThat(adapter.supportsEventType(createPayloadEventType(ResolvableType.forClassWithGenerics(List.class, String.class))))
.as("handleGenericAnyPayload(EntityWrapper<?>) with List<String>").isFalse();
}
@Test // gh-30399
void genericApplicationEventSupportsSpecificType() throws Exception {
Method method = SampleEvents.class.getDeclaredMethod("handleGenericString", GenericTestEvent.class);
ApplicationListenerMethodAdapter adapter = createTestInstance(method);
assertThat(adapter.supportsEventType(ResolvableType.forClass(GenericTestEvent.class)))
.as("handleGenericString(GenericTestEvent<String>) with GenericTestEvent<?>").isTrue();
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(GenericTestEvent.class, Integer.class)))
.as("handleGenericString(GenericTestEvent<String>) with GenericTestEvent<Integer>").isFalse();
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(GenericTestEvent.class, String.class)))
.as("handleGenericString(GenericTestEvent<String>) with GenericTestEvent<String>").isTrue();
}
@Test // gh-30399
void genericRawApplicationEventSupportsRawTypeAndAnySpecificType() throws Exception {
Method method = SampleEvents.class.getDeclaredMethod("handleGenericRaw", GenericTestEvent.class);
ApplicationListenerMethodAdapter adapter = createTestInstance(method);
assertThat(adapter.supportsEventType(ResolvableType.forClass(GenericTestEvent.class)))
.as("handleGenericRaw(GenericTestEvent<?>) with GenericTestEvent<?>").isTrue();
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(GenericTestEvent.class, String.class)))
.as("handleGenericRaw(GenericTestEvent<?>) with GenericTestEvent<String>").isTrue();
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(GenericTestEvent.class, Integer.class)))
.as("handleGenericRaw(GenericTestEvent<?>) with GenericTestEvent<Integer>").isTrue();
}
@Test // gh-30399
void unrelatedApplicationEventDoesNotSupportRawTypeOrAnySpecificType() throws Exception {
Method method = SampleEvents.class.getDeclaredMethod("handleUnrelated", ContextRefreshedEvent.class);
ApplicationListenerMethodAdapter adapter = createTestInstance(method);
assertThat(adapter.supportsEventType(ResolvableType.forClass(GenericTestEvent.class)))
.as("handleUnrelated(ContextRefreshedEvent) with GenericTestEvent<?>").isTrue(); // known bug in 5.3.x
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(GenericTestEvent.class, String.class)))
.as("handleUnrelated(ContextRefreshedEvent) with GenericTestEvent<String>").isFalse();
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(GenericTestEvent.class, Integer.class)))
.as("handleUnrelated(ContextRefreshedEvent) with GenericTestEvent<Integer>").isFalse();
}
private void supportsEventType(boolean match, Method method, ResolvableType eventType) {
ApplicationListenerMethodAdapter adapter = createTestInstance(method);
@@ -341,7 +433,11 @@ public class ApplicationListenerMethodAdapterTests extends AbstractApplicationEv
return new StaticApplicationListenerMethodAdapter(method, this.sampleEvents);
}
private ResolvableType createGenericEventType(Class<?> payloadType) {
private ResolvableType createPayloadEventType(Class<?> payloadType) {
return ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class, payloadType);
}
private ResolvableType createPayloadEventType(ResolvableType payloadType) {
return ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class, payloadType);
}
@@ -373,6 +469,14 @@ public class ApplicationListenerMethodAdapterTests extends AbstractApplicationEv
public void handleGenericString(GenericTestEvent<String> event) {
}
@EventListener
public void handleGenericRaw(GenericTestEvent<?> event) {
}
@EventListener
public void handleUnrelated(ContextRefreshedEvent event) {
}
@EventListener
public void handleString(String payload) {
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -85,16 +85,13 @@ public class ReflectiveLoadTimeWeaverTests {
private int numTimesAddTransformerCalled = 0;
public int getNumTimesGetThrowawayClassLoaderCalled() {
return this.numTimesAddTransformerCalled;
}
public void addTransformer(ClassFileTransformer transformer) {
++this.numTimesAddTransformerCalled;
}
}
@@ -102,18 +99,15 @@ public class ReflectiveLoadTimeWeaverTests {
private int numTimesGetThrowawayClassLoaderCalled = 0;
@Override
public int getNumTimesGetThrowawayClassLoaderCalled() {
return this.numTimesGetThrowawayClassLoaderCalled;
}
public ClassLoader getThrowawayClassLoader() {
++this.numTimesGetThrowawayClassLoaderCalled;
return getClass().getClassLoader();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -95,9 +95,8 @@ public class EnableAsyncTests {
public void properExceptionForExistingProxyDependencyMismatch() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AsyncConfig.class, AsyncBeanWithInterface.class, AsyncBeanUser.class);
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
ctx::refresh)
.withCauseInstanceOf(BeanNotOfRequiredTypeException.class);
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(ctx::refresh)
.withCauseInstanceOf(BeanNotOfRequiredTypeException.class);
ctx.close();
}
@@ -105,9 +104,8 @@ public class EnableAsyncTests {
public void properExceptionForResolvedProxyDependencyMismatch() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AsyncConfig.class, AsyncBeanUser.class, AsyncBeanWithInterface.class);
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
ctx::refresh)
.withCauseInstanceOf(BeanNotOfRequiredTypeException.class);
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(ctx::refresh)
.withCauseInstanceOf(BeanNotOfRequiredTypeException.class);
ctx.close();
}
@@ -182,8 +180,7 @@ public class EnableAsyncTests {
@SuppressWarnings("resource")
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AspectJAsyncAnnotationConfig.class);
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(
ctx::refresh);
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(ctx::refresh);
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -61,7 +61,7 @@ public class EnableSchedulingTests {
@EnabledForTestGroups(LONG_RUNNING)
public void withFixedRateTask() throws InterruptedException {
ctx = new AnnotationConfigApplicationContext(FixedRateTaskConfig.class);
assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks().size()).isEqualTo(2);
assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks()).hasSize(2);
Thread.sleep(100);
assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThanOrEqualTo(10);
@@ -71,7 +71,7 @@ public class EnableSchedulingTests {
@EnabledForTestGroups(LONG_RUNNING)
public void withSubclass() throws InterruptedException {
ctx = new AnnotationConfigApplicationContext(FixedRateTaskConfigSubclass.class);
assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks().size()).isEqualTo(2);
assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks()).hasSize(2);
Thread.sleep(100);
assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThanOrEqualTo(10);
@@ -81,13 +81,13 @@ public class EnableSchedulingTests {
@EnabledForTestGroups(LONG_RUNNING)
public void withExplicitScheduler() throws InterruptedException {
ctx = new AnnotationConfigApplicationContext(ExplicitSchedulerConfig.class);
assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks().size()).isEqualTo(1);
assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks()).hasSize(1);
Thread.sleep(100);
assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThanOrEqualTo(10);
assertThat(ctx.getBean(ExplicitSchedulerConfig.class).threadName).startsWith("explicitScheduler-");
assertThat(Arrays.asList(ctx.getDefaultListableBeanFactory().getDependentBeans("myTaskScheduler")).contains(
TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)).isTrue();
TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)).isTrue();
}
@Test
@@ -100,7 +100,7 @@ public class EnableSchedulingTests {
@EnabledForTestGroups(LONG_RUNNING)
public void withExplicitScheduledTaskRegistrar() throws InterruptedException {
ctx = new AnnotationConfigApplicationContext(ExplicitScheduledTaskRegistrarConfig.class);
assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks().size()).isEqualTo(1);
assertThat(ctx.getBean(ScheduledTaskHolder.class).getScheduledTasks()).hasSize(1);
Thread.sleep(100);
assertThat(ctx.getBean(AtomicInteger.class).get()).isGreaterThanOrEqualTo(10);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,7 +55,7 @@ abstract class AbstractSchedulingTaskExecutorTests {
@BeforeEach
void setUp(TestInfo testInfo) {
void setup(TestInfo testInfo) {
this.testName = testInfo.getTestMethod().get().getName();
this.threadNamePrefix = this.testName + "-";
this.executor = buildExecutor();
@@ -84,11 +84,11 @@ abstract class AbstractSchedulingTaskExecutorTests {
TestTask task = new TestTask(this.testName, 0);
executor.execute(task);
Awaitility.await()
.dontCatchUncaughtExceptions()
.atMost(1, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.until(() -> task.exception.get() != null && task.exception.get().getMessage().equals(
"TestTask failure for test 'executeFailingRunnable': expectedRunCount:<0>, actualRunCount:<1>"));
.dontCatchUncaughtExceptions()
.atMost(1, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.until(() -> task.exception.get() != null && task.exception.get().getMessage().equals(
"TestTask failure for test 'executeFailingRunnable': expectedRunCount:<0>, actualRunCount:<1>"));
}
@Test
@@ -101,7 +101,7 @@ abstract class AbstractSchedulingTaskExecutorTests {
}
@Test
void submitFailingRunnable() throws Exception {
void submitFailingRunnable() {
TestTask task = new TestTask(this.testName, 0);
Future<?> future = executor.submit(task);
assertThatExceptionOfType(ExecutionException.class).isThrownBy(() ->
@@ -121,31 +121,31 @@ abstract class AbstractSchedulingTaskExecutorTests {
}
@Test
void submitListenableRunnable() throws Exception {
void submitListenableRunnable() {
TestTask task = new TestTask(this.testName, 1);
// Act
ListenableFuture<?> future = executor.submitListenable(task);
future.addCallback(result -> outcome = result, ex -> outcome = ex);
// Assert
Awaitility.await()
.atMost(1, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.until(future::isDone);
.atMost(1, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.until(future::isDone);
assertThat(outcome).isNull();
assertThreadNamePrefix(task);
}
@Test
void submitFailingListenableRunnable() throws Exception {
void submitFailingListenableRunnable() {
TestTask task = new TestTask(this.testName, 0);
ListenableFuture<?> future = executor.submitListenable(task);
future.addCallback(result -> outcome = result, ex -> outcome = ex);
Awaitility.await()
.dontCatchUncaughtExceptions()
.atMost(1, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.until(() -> future.isDone() && outcome != null);
.dontCatchUncaughtExceptions()
.atMost(1, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.until(() -> future.isDone() && outcome != null);
assertThat(outcome.getClass()).isSameAs(RuntimeException.class);
}
@@ -159,14 +159,13 @@ abstract class AbstractSchedulingTaskExecutorTests {
future1.get(1000, TimeUnit.MILLISECONDS);
}
catch (Exception ex) {
/* ignore */
// ignore
}
Awaitility.await()
.atMost(4, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.untilAsserted(() ->
assertThatExceptionOfType(CancellationException.class).isThrownBy(() ->
future2.get(1000, TimeUnit.MILLISECONDS)));
.atMost(4, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.untilAsserted(() -> assertThatExceptionOfType(CancellationException.class)
.isThrownBy(() -> future2.get(1000, TimeUnit.MILLISECONDS)));
}
@Test
@@ -178,11 +177,11 @@ abstract class AbstractSchedulingTaskExecutorTests {
}
@Test
void submitFailingCallable() throws Exception {
void submitFailingCallable() {
TestCallable task = new TestCallable(this.testName, 0);
Future<String> future = executor.submit(task);
assertThatExceptionOfType(ExecutionException.class).isThrownBy(() ->
future.get(1000, TimeUnit.MILLISECONDS));
assertThatExceptionOfType(ExecutionException.class)
.isThrownBy(() -> future.get(1000, TimeUnit.MILLISECONDS));
assertThat(future.isDone()).isTrue();
}
@@ -196,42 +195,41 @@ abstract class AbstractSchedulingTaskExecutorTests {
future1.get(1000, TimeUnit.MILLISECONDS);
}
catch (Exception ex) {
/* ignore */
// ignore
}
Awaitility.await()
.atMost(4, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.untilAsserted(() ->
assertThatExceptionOfType(CancellationException.class).isThrownBy(() ->
future2.get(1000, TimeUnit.MILLISECONDS)));
.atMost(4, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.untilAsserted(() -> assertThatExceptionOfType(CancellationException.class)
.isThrownBy(() -> future2.get(1000, TimeUnit.MILLISECONDS)));
}
@Test
void submitListenableCallable() throws Exception {
void submitListenableCallable() {
TestCallable task = new TestCallable(this.testName, 1);
// Act
ListenableFuture<String> future = executor.submitListenable(task);
future.addCallback(result -> outcome = result, ex -> outcome = ex);
// Assert
Awaitility.await()
.atMost(1, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.until(() -> future.isDone() && outcome != null);
.atMost(1, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.until(() -> future.isDone() && outcome != null);
assertThat(outcome.toString().substring(0, this.threadNamePrefix.length())).isEqualTo(this.threadNamePrefix);
}
@Test
void submitFailingListenableCallable() throws Exception {
void submitFailingListenableCallable() {
TestCallable task = new TestCallable(this.testName, 0);
// Act
ListenableFuture<String> future = executor.submitListenable(task);
future.addCallback(result -> outcome = result, ex -> outcome = ex);
// Assert
Awaitility.await()
.dontCatchUncaughtExceptions()
.atMost(1, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.until(() -> future.isDone() && outcome != null);
.dontCatchUncaughtExceptions()
.atMost(1, TimeUnit.SECONDS)
.pollInterval(10, TimeUnit.MILLISECONDS)
.until(() -> future.isDone() && outcome != null);
assertThat(outcome.getClass()).isSameAs(RuntimeException.class);
}
@@ -296,8 +294,9 @@ abstract class AbstractSchedulingTaskExecutorTests {
}
if (expectedRunCount >= 0) {
if (actualRunCount.incrementAndGet() > expectedRunCount) {
RuntimeException exception = new RuntimeException(String.format("%s failure for test '%s': expectedRunCount:<%d>, actualRunCount:<%d>",
getClass().getSimpleName(), this.testName, expectedRunCount, actualRunCount.get()));
RuntimeException exception = new RuntimeException(String.format(
"%s failure for test '%s': expectedRunCount:<%d>, actualRunCount:<%d>",
getClass().getSimpleName(), this.testName, expectedRunCount, actualRunCount.get()));
this.exception.set(exception);
throw exception;
}
@@ -329,8 +328,9 @@ abstract class AbstractSchedulingTaskExecutorTests {
}
if (expectedRunCount >= 0) {
if (actualRunCount.incrementAndGet() > expectedRunCount) {
throw new RuntimeException(String.format("%s failure for test '%s': expectedRunCount:<%d>, actualRunCount:<%d>",
getClass().getSimpleName(), this.testName, expectedRunCount, actualRunCount.get()));
throw new RuntimeException(String.format(
"%s failure for test '%s': expectedRunCount:<%d>, actualRunCount:<%d>",
getClass().getSimpleName(), this.testName, expectedRunCount, actualRunCount.get()));
}
}
return Thread.currentThread().getName();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.scheduling.concurrent;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.ThreadPoolExecutor;
@@ -26,6 +27,8 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.core.task.NoOpRunnable;
import org.springframework.core.task.TaskDecorator;
import org.springframework.util.Assert;
import static org.assertj.core.api.Assertions.assertThatCode;
@@ -69,10 +72,45 @@ class ConcurrentTaskExecutorTests extends AbstractSchedulingTaskExecutorTests {
}
@Test
void passingNullExecutorToSetterResultsInDefaultTaskExecutorBeingUsed() {
void earlySetConcurrentExecutorCallRespectsConfiguredTaskDecorator() {
ConcurrentTaskExecutor executor = new ConcurrentTaskExecutor();
executor.setConcurrentExecutor(null);
executor.setConcurrentExecutor(new DecoratedExecutor());
executor.setTaskDecorator(new RunnableDecorator());
assertThatCode(() -> executor.execute(new NoOpRunnable())).doesNotThrowAnyException();
}
@Test
void lateSetConcurrentExecutorCallRespectsConfiguredTaskDecorator() {
ConcurrentTaskExecutor executor = new ConcurrentTaskExecutor();
executor.setTaskDecorator(new RunnableDecorator());
executor.setConcurrentExecutor(new DecoratedExecutor());
assertThatCode(() -> executor.execute(new NoOpRunnable())).doesNotThrowAnyException();
}
private static class DecoratedRunnable implements Runnable {
@Override
public void run() {
}
}
private static class RunnableDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
return new DecoratedRunnable();
}
}
private static class DecoratedExecutor implements Executor {
@Override
public void execute(Runnable command) {
Assert.state(command instanceof DecoratedRunnable, "TaskDecorator not applied");
}
}
}
@@ -41,14 +41,14 @@ import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING;
class ScheduledExecutorFactoryBeanTests {
@Test
void throwsExceptionIfPoolSizeIsLessThanZero() throws Exception {
void throwsExceptionIfPoolSizeIsLessThanZero() {
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean();
assertThatIllegalArgumentException().isThrownBy(() -> factory.setPoolSize(-1));
}
@Test
@SuppressWarnings("serial")
void shutdownNowIsPropagatedToTheExecutorOnDestroy() throws Exception {
void shutdownNowIsPropagatedToTheExecutorOnDestroy() {
final ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() {
@@ -66,7 +66,7 @@ class ScheduledExecutorFactoryBeanTests {
@Test
@SuppressWarnings("serial")
void shutdownIsPropagatedToTheExecutorOnDestroy() throws Exception {
void shutdownIsPropagatedToTheExecutorOnDestroy() {
final ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() {
@@ -85,7 +85,7 @@ class ScheduledExecutorFactoryBeanTests {
@Test
@EnabledForTestGroups(LONG_RUNNING)
void oneTimeExecutionIsSetUpAndFiresCorrectly() throws Exception {
void oneTimeExecutionIsSetUpAndFiresCorrectly() {
Runnable runnable = mock(Runnable.class);
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean();
@@ -99,7 +99,7 @@ class ScheduledExecutorFactoryBeanTests {
@Test
@EnabledForTestGroups(LONG_RUNNING)
void fixedRepeatedExecutionIsSetUpAndFiresCorrectly() throws Exception {
void fixedRepeatedExecutionIsSetUpAndFiresCorrectly() {
Runnable runnable = mock(Runnable.class);
ScheduledExecutorTask task = new ScheduledExecutorTask(runnable);
@@ -117,7 +117,7 @@ class ScheduledExecutorFactoryBeanTests {
@Test
@EnabledForTestGroups(LONG_RUNNING)
void fixedRepeatedExecutionIsSetUpAndFiresCorrectlyAfterException() throws Exception {
void fixedRepeatedExecutionIsSetUpAndFiresCorrectlyAfterException() {
Runnable runnable = mock(Runnable.class);
willThrow(new IllegalStateException()).given(runnable).run();
@@ -137,7 +137,7 @@ class ScheduledExecutorFactoryBeanTests {
@Test
@EnabledForTestGroups(LONG_RUNNING)
void withInitialDelayRepeatedExecutionIsSetUpAndFiresCorrectly() throws Exception {
void withInitialDelayRepeatedExecutionIsSetUpAndFiresCorrectly() {
Runnable runnable = mock(Runnable.class);
ScheduledExecutorTask task = new ScheduledExecutorTask(runnable);
@@ -157,7 +157,7 @@ class ScheduledExecutorFactoryBeanTests {
@Test
@EnabledForTestGroups(LONG_RUNNING)
void withInitialDelayRepeatedExecutionIsSetUpAndFiresCorrectlyAfterException() throws Exception {
void withInitialDelayRepeatedExecutionIsSetUpAndFiresCorrectlyAfterException() {
Runnable runnable = mock(Runnable.class);
willThrow(new IllegalStateException()).given(runnable).run();
@@ -179,7 +179,7 @@ class ScheduledExecutorFactoryBeanTests {
@Test
@SuppressWarnings("serial")
void settingThreadFactoryToNullForcesUseOfDefaultButIsOtherwiseCool() throws Exception {
void settingThreadFactoryToNullForcesUseOfDefaultButIsOtherwiseCool() {
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() {
@Override
protected ScheduledExecutorService createExecutor(int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
@@ -195,7 +195,7 @@ class ScheduledExecutorFactoryBeanTests {
@Test
@SuppressWarnings("serial")
void settingRejectedExecutionHandlerToNullForcesUseOfDefaultButIsOtherwiseCool() throws Exception {
void settingRejectedExecutionHandlerToNullForcesUseOfDefaultButIsOtherwiseCool() {
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() {
@Override
protected ScheduledExecutorService createExecutor(int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
@@ -210,7 +210,7 @@ class ScheduledExecutorFactoryBeanTests {
}
@Test
void objectTypeReportsCorrectType() throws Exception {
void objectTypeReportsCorrectType() {
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean();
assertThat(factory.getObjectType()).isEqualTo(ScheduledExecutorService.class);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,8 +26,8 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
/**
@@ -67,8 +67,7 @@ class ThreadPoolTaskExecutorTests extends AbstractSchedulingTaskExecutorTests {
assertThat(executor.getCorePoolSize()).isEqualTo(1);
assertThat(executor.getThreadPoolExecutor().getCorePoolSize()).isEqualTo(1);
assertThatThrownBy(() -> executor.setCorePoolSize(-1))
.isInstanceOf(IllegalArgumentException.class);
assertThatIllegalArgumentException().isThrownBy(() -> executor.setCorePoolSize(-1));
assertThat(executor.getCorePoolSize()).isEqualTo(1);
assertThat(executor.getThreadPoolExecutor().getCorePoolSize()).isEqualTo(1);
@@ -90,8 +89,7 @@ class ThreadPoolTaskExecutorTests extends AbstractSchedulingTaskExecutorTests {
assertThat(executor.getMaxPoolSize()).isEqualTo(1);
assertThat(executor.getThreadPoolExecutor().getMaximumPoolSize()).isEqualTo(1);
assertThatThrownBy(() -> executor.setMaxPoolSize(0))
.isInstanceOf(IllegalArgumentException.class);
assertThatIllegalArgumentException().isThrownBy(() -> executor.setMaxPoolSize(0));
assertThat(executor.getMaxPoolSize()).isEqualTo(1);
assertThat(executor.getThreadPoolExecutor().getMaximumPoolSize()).isEqualTo(1);
@@ -113,8 +111,7 @@ class ThreadPoolTaskExecutorTests extends AbstractSchedulingTaskExecutorTests {
assertThat(executor.getKeepAliveSeconds()).isEqualTo(60);
assertThat(executor.getThreadPoolExecutor().getKeepAliveTime(TimeUnit.SECONDS)).isEqualTo(60);
assertThatThrownBy(() -> executor.setKeepAliveSeconds(-10))
.isInstanceOf(IllegalArgumentException.class);
assertThatIllegalArgumentException().isThrownBy(() -> executor.setKeepAliveSeconds(-10));
assertThat(executor.getKeepAliveSeconds()).isEqualTo(60);
assertThat(executor.getThreadPoolExecutor().getKeepAliveTime(TimeUnit.SECONDS)).isEqualTo(60);
@@ -124,8 +121,8 @@ class ThreadPoolTaskExecutorTests extends AbstractSchedulingTaskExecutorTests {
void queueCapacityDefault() {
assertThat(executor.getQueueCapacity()).isEqualTo(Integer.MAX_VALUE);
assertThat(executor.getThreadPoolExecutor().getQueue())
.asInstanceOf(type(LinkedBlockingQueue.class))
.extracting(BlockingQueue::remainingCapacity).isEqualTo(Integer.MAX_VALUE);
.asInstanceOf(type(LinkedBlockingQueue.class))
.extracting(BlockingQueue::remainingCapacity).isEqualTo(Integer.MAX_VALUE);
}
@Test
@@ -135,8 +132,8 @@ class ThreadPoolTaskExecutorTests extends AbstractSchedulingTaskExecutorTests {
assertThat(executor.getQueueCapacity()).isZero();
assertThat(executor.getThreadPoolExecutor().getQueue())
.asInstanceOf(type(SynchronousQueue.class))
.extracting(BlockingQueue::remainingCapacity).isEqualTo(0);
.asInstanceOf(type(SynchronousQueue.class))
.extracting(BlockingQueue::remainingCapacity).isEqualTo(0);
}
@Test
@@ -576,15 +576,17 @@ public class ReflectUtils {
c = (Class) lookupDefineClassMethod.invoke(lookup, b);
}
catch (InvocationTargetException ex) {
throw new CodeGenerationException(ex.getTargetException());
}
catch (IllegalAccessException ex) {
throw new CodeGenerationException(ex) {
Throwable target = ex.getTargetException();
if (target.getClass() != LinkageError.class && target.getClass() != IllegalAccessException.class) {
throw new CodeGenerationException(target);
}
throw new CodeGenerationException(target) {
@Override
public String getMessage() {
return "ClassLoader mismatch for [" + contextClass.getName() +
"]: JVM should be started with --add-opens=java.base/java.lang=ALL-UNNAMED " +
"for ClassLoader.defineClass to be accessible on " + loader.getClass().getName();
"for ClassLoader.defineClass to be accessible on " + loader.getClass().getName() +
"; consider co-locating the affected class in that target ClassLoader instead.";
}
};
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -68,6 +68,7 @@ public final class CollectionFactory {
approximableCollectionTypes.add(SortedSet.class);
approximableCollectionTypes.add(NavigableSet.class);
approximableMapTypes.add(Map.class);
approximableMapTypes.add(MultiValueMap.class);
approximableMapTypes.add(SortedMap.class);
approximableMapTypes.add(NavigableMap.class);
@@ -80,6 +81,7 @@ public final class CollectionFactory {
approximableCollectionTypes.add(EnumSet.class);
approximableMapTypes.add(HashMap.class);
approximableMapTypes.add(LinkedHashMap.class);
approximableMapTypes.add(LinkedMultiValueMap.class);
approximableMapTypes.add(TreeMap.class);
approximableMapTypes.add(EnumMap.class);
}
@@ -121,13 +123,7 @@ public final class CollectionFactory {
*/
@SuppressWarnings({"rawtypes", "unchecked", "cast"})
public static <E> Collection<E> createApproximateCollection(@Nullable Object collection, int capacity) {
if (collection instanceof LinkedList) {
return new LinkedList<>();
}
else if (collection instanceof List) {
return new ArrayList<>(capacity);
}
else if (collection instanceof EnumSet) {
if (collection instanceof EnumSet) {
// Cast is necessary for compilation in Eclipse 4.4.1.
Collection<E> enumSet = (Collection<E>) EnumSet.copyOf((EnumSet) collection);
enumSet.clear();
@@ -136,6 +132,12 @@ public final class CollectionFactory {
else if (collection instanceof SortedSet) {
return new TreeSet<>(((SortedSet<E>) collection).comparator());
}
if (collection instanceof LinkedList) {
return new LinkedList<>();
}
else if (collection instanceof List) {
return new ArrayList<>(capacity);
}
else {
return new LinkedHashSet<>(capacity);
}
@@ -181,7 +183,7 @@ public final class CollectionFactory {
@SuppressWarnings({"unchecked", "cast"})
public static <E> Collection<E> createCollection(Class<?> collectionType, @Nullable Class<?> elementType, int capacity) {
Assert.notNull(collectionType, "Collection type must not be null");
if (LinkedHashSet.class == collectionType || HashSet.class == collectionType ||
if (LinkedHashSet.class == collectionType ||
Set.class == collectionType || Collection.class == collectionType) {
return new LinkedHashSet<>(capacity);
}
@@ -191,8 +193,8 @@ public final class CollectionFactory {
else if (LinkedList.class == collectionType) {
return new LinkedList<>();
}
else if (TreeSet.class == collectionType || NavigableSet.class == collectionType
|| SortedSet.class == collectionType) {
else if (TreeSet.class == collectionType || NavigableSet.class == collectionType ||
SortedSet.class == collectionType) {
return new TreeSet<>();
}
else if (EnumSet.class.isAssignableFrom(collectionType)) {
@@ -200,6 +202,9 @@ public final class CollectionFactory {
// Cast is necessary for compilation in Eclipse 4.4.1.
return (Collection<E>) EnumSet.noneOf(asEnumType(elementType));
}
else if (HashSet.class == collectionType) {
return new HashSet<>(capacity);
}
else {
if (collectionType.isInterface() || !Collection.class.isAssignableFrom(collectionType)) {
throw new IllegalArgumentException("Unsupported Collection type: " + collectionType.getName());
@@ -251,6 +256,9 @@ public final class CollectionFactory {
else if (map instanceof SortedMap) {
return new TreeMap<>(((SortedMap<K, V>) map).comparator());
}
else if (map instanceof MultiValueMap) {
return new LinkedMultiValueMap(capacity);
}
else {
return new LinkedHashMap<>(capacity);
}
@@ -297,26 +305,24 @@ public final class CollectionFactory {
@SuppressWarnings({"rawtypes", "unchecked"})
public static <K, V> Map<K, V> createMap(Class<?> mapType, @Nullable Class<?> keyType, int capacity) {
Assert.notNull(mapType, "Map type must not be null");
if (mapType.isInterface()) {
if (Map.class == mapType) {
return new LinkedHashMap<>(capacity);
}
else if (SortedMap.class == mapType || NavigableMap.class == mapType) {
return new TreeMap<>();
}
else if (MultiValueMap.class == mapType) {
return new LinkedMultiValueMap();
}
else {
throw new IllegalArgumentException("Unsupported Map interface: " + mapType.getName());
}
if (LinkedHashMap.class == mapType || Map.class == mapType) {
return new LinkedHashMap<>(capacity);
}
else if (LinkedMultiValueMap.class == mapType || MultiValueMap.class == mapType) {
return new LinkedMultiValueMap();
}
else if (TreeMap.class == mapType || SortedMap.class == mapType || NavigableMap.class == mapType) {
return new TreeMap<>();
}
else if (EnumMap.class == mapType) {
Assert.notNull(keyType, "Cannot create EnumMap for unknown key type");
return new EnumMap(asEnumType(keyType));
}
else if (HashMap.class == mapType) {
return new HashMap<>(capacity);
}
else {
if (!Map.class.isAssignableFrom(mapType)) {
if (mapType.isInterface() || !Map.class.isAssignableFrom(mapType)) {
throw new IllegalArgumentException("Unsupported Map type: " + mapType.getName());
}
try {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -781,6 +781,7 @@ public class MethodParameter {
return new MethodParameter(this);
}
/**
* Create a new MethodParameter for the given method or constructor.
* <p>This is a convenience factory method for scenarios where a
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -133,6 +133,9 @@ public class ResolvableType implements Serializable {
@Nullable
private volatile ResolvableType[] generics;
@Nullable
private volatile Boolean unresolvableGenerics;
/**
* Private constructor used to create a new {@link ResolvableType} for cache key purposes,
@@ -545,6 +548,15 @@ public class ResolvableType implements Serializable {
if (this == NONE) {
return false;
}
Boolean unresolvableGenerics = this.unresolvableGenerics;
if (unresolvableGenerics == null) {
unresolvableGenerics = determineUnresolvableGenerics();
this.unresolvableGenerics = unresolvableGenerics;
}
return unresolvableGenerics;
}
private boolean determineUnresolvableGenerics() {
ResolvableType[] generics = getGenerics();
for (ResolvableType generic : generics) {
if (generic.isUnresolvableTypeVariable() || generic.isWildcardWithoutBounds()) {
@@ -556,7 +568,7 @@ public class ResolvableType implements Serializable {
try {
for (Type genericInterface : resolved.getGenericInterfaces()) {
if (genericInterface instanceof Class) {
if (forClass((Class<?>) genericInterface).hasGenerics()) {
if (((Class<?>) genericInterface).getTypeParameters().length > 0) {
return true;
}
}
@@ -565,7 +577,10 @@ public class ResolvableType implements Serializable {
catch (TypeNotPresentException ex) {
// Ignore non-present types in generic signature
}
return getSuperType().hasUnresolvableGenerics();
Class<?> superclass = resolved.getSuperclass();
if (superclass != null && superclass != Object.class) {
return getSuperType().hasUnresolvableGenerics();
}
}
return false;
}
@@ -22,7 +22,6 @@ import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
@@ -204,19 +203,18 @@ final class SerializableTypeWrapper {
return forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, -1));
}
else if (Type[].class == method.getReturnType() && ObjectUtils.isEmpty(args)) {
Type[] result = new Type[((Type[]) method.invoke(this.provider.getType())).length];
Object returnValue = ReflectionUtils.invokeMethod(method, this.provider.getType());
if (returnValue == null) {
return null;
}
Type[] result = new Type[((Type[]) returnValue).length];
for (int i = 0; i < result.length; i++) {
result[i] = forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, i));
}
return result;
}
try {
return method.invoke(this.provider.getType(), args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
return ReflectionUtils.invokeMethod(method, this.provider.getType(), args);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -149,8 +149,8 @@ public abstract class AnnotationUtils {
* @since 5.2
* @see #isCandidateClass(Class, String)
*/
public static boolean isCandidateClass(Class<?> clazz, Class<? extends Annotation> annotationType) {
return isCandidateClass(clazz, annotationType.getName());
public static boolean isCandidateClass(Class<?> clazz, @Nullable Class<? extends Annotation> annotationType) {
return (annotationType != null && isCandidateClass(clazz, annotationType.getName()));
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,6 +57,8 @@ package org.springframework.core.env;
* of property sources prior to application context {@code refresh()}.
*
* @author Chris Beams
* @author Phillip Webb
* @author Sam Brannen
* @since 3.1
* @see PropertyResolver
* @see EnvironmentCapable
@@ -95,25 +97,56 @@ public interface Environment extends PropertyResolver {
String[] getDefaultProfiles();
/**
* Return whether one or more of the given profiles is active or, in the case of no
* explicit active profiles, whether one or more of the given profiles is included in
* the set of default profiles. If a profile begins with '!' the logic is inverted,
* i.e. the method will return {@code true} if the given profile is <em>not</em> active.
* For example, {@code env.acceptsProfiles("p1", "!p2")} will return {@code true} if
* profile 'p1' is active or 'p2' is not active.
* @throws IllegalArgumentException if called with zero arguments
* or if any profile is {@code null}, empty, or whitespace only
* Determine whether one of the given profile expressions matches the
* {@linkplain #getActiveProfiles() active profiles} &mdash; or in the case
* of no explicit active profiles, whether one of the given profile expressions
* matches the {@linkplain #getDefaultProfiles() default profiles}.
* <p>Profile expressions allow for complex, boolean profile logic to be
* expressed &mdash; for example {@code "p1 & p2"}, {@code "(p1 & p2) | p3"},
* etc. See {@link Profiles#of(String...)} for details on the supported
* expression syntax.
* <p>This method is a convenient shortcut for
* {@code env.acceptsProfiles(Profiles.of(profileExpressions))}.
* @since 5.3.28
* @see Profiles#of(String...)
* @see #acceptsProfiles(Profiles)
*/
default boolean matchesProfiles(String... profileExpressions) {
return acceptsProfiles(Profiles.of(profileExpressions));
}
/**
* Determine whether one or more of the given profiles is active &mdash; or
* in the case of no explicit {@linkplain #getActiveProfiles() active profiles},
* whether one or more of the given profiles is included in the set of
* {@linkplain #getDefaultProfiles() default profiles}.
* <p>If a profile begins with '!' the logic is inverted, meaning this method
* will return {@code true} if the given profile is <em>not</em> active. For
* example, {@code env.acceptsProfiles("p1", "!p2")} will return {@code true}
* if profile 'p1' is active or 'p2' is not active.
* @throws IllegalArgumentException if called with a {@code null} array, an
* empty array, zero arguments or if any profile is {@code null}, empty, or
* whitespace only
* @see #getActiveProfiles
* @see #getDefaultProfiles
* @see #matchesProfiles(String...)
* @see #acceptsProfiles(Profiles)
* @deprecated as of 5.1 in favor of {@link #acceptsProfiles(Profiles)}
* @deprecated as of 5.1 in favor of {@link #acceptsProfiles(Profiles)} or
* {@link #matchesProfiles(String...)}
*/
@Deprecated
boolean acceptsProfiles(String... profiles);
/**
* Return whether the {@linkplain #getActiveProfiles() active profiles}
* match the given {@link Profiles} predicate.
* Determine whether the given {@link Profiles} predicate matches the
* {@linkplain #getActiveProfiles() active profiles} &mdash; or in the case
* of no explicit active profiles, whether the given {@code Profiles} predicate
* matches the {@linkplain #getDefaultProfiles() default profiles}.
* <p>If you wish provide profile expressions directly as strings, use
* {@link #matchesProfiles(String...)} instead.
* @since 5.1
* @see #matchesProfiles(String...)
* @see Profiles#of(String...)
*/
boolean acceptsProfiles(Profiles profiles);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,32 +43,32 @@ public interface Profiles {
/**
* Create a new {@link Profiles} instance that checks for matches against
* the given <em>profile strings</em>.
* the given <em>profile expressions</em>.
* <p>The returned instance will {@linkplain Profiles#matches(Predicate) match}
* if any one of the given profile strings matches.
* <p>A profile string may contain a simple profile name (for example
* {@code "production"}) or a profile expression. A profile expression allows
* if any one of the given profile expressions matches.
* <p>A profile expression may contain a simple profile name (for example
* {@code "production"}) or a compound expression. A compound expression allows
* for more complicated profile logic to be expressed, for example
* {@code "production & cloud"}.
* <p>The following operators are supported in profile expressions.
* <ul>
* <li>{@code !} - A logical <em>NOT</em> of the profile or profile expression</li>
* <li>{@code &} - A logical <em>AND</em> of the profiles or profile expressions</li>
* <li>{@code |} - A logical <em>OR</em> of the profiles or profile expressions</li>
* <li>{@code !} - A logical <em>NOT</em> of the profile name or compound expression</li>
* <li>{@code &} - A logical <em>AND</em> of the profile names or compound expressions</li>
* <li>{@code |} - A logical <em>OR</em> of the profile names or compound expressions</li>
* </ul>
* <p>Please note that the {@code &} and {@code |} operators may not be mixed
* without using parentheses. For example {@code "a & b | c"} is not a valid
* expression; it must be expressed as {@code "(a & b) | c"} or
* without using parentheses. For example, {@code "a & b | c"} is not a valid
* expression: it must be expressed as {@code "(a & b) | c"} or
* {@code "a & (b | c)"}.
* <p>As of Spring Framework 5.1.17, two {@code Profiles} instances returned
* by this method are considered equivalent to each other (in terms of
* {@code equals()} and {@code hashCode()} semantics) if they are created
* with identical <em>profile strings</em>.
* @param profiles the <em>profile strings</em> to include
* with identical <em>profile expressions</em>.
* @param profileExpressions the <em>profile expressions</em> to include
* @return a new {@link Profiles} instance
*/
static Profiles of(String... profiles) {
return ProfilesParser.parse(profiles);
static Profiles of(String... profileExpressions) {
return ProfilesParser.parse(profileExpressions);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,7 +43,7 @@ final class ProfilesParser {
static Profiles parse(String... expressions) {
Assert.notEmpty(expressions, "Must specify at least one profile");
Assert.notEmpty(expressions, "Must specify at least one profile expression");
Profiles[] parsed = new Profiles[expressions.length];
for (int i = 0; i < expressions.length; i++) {
parsed[i] = parseExpression(expressions[i]);
@@ -71,8 +71,8 @@ final class ProfilesParser {
}
switch (token) {
case "(":
Profiles contents = parseTokens(expression, tokens, Context.BRACKET);
if (context == Context.INVERT) {
Profiles contents = parseTokens(expression, tokens, Context.PARENTHESIS);
if (context == Context.NEGATE) {
return contents;
}
elements.add(contents);
@@ -86,11 +86,11 @@ final class ProfilesParser {
operator = Operator.OR;
break;
case "!":
elements.add(not(parseTokens(expression, tokens, Context.INVERT)));
elements.add(not(parseTokens(expression, tokens, Context.NEGATE)));
break;
case ")":
Profiles merged = merge(expression, elements, operator);
if (context == Context.BRACKET) {
if (context == Context.PARENTHESIS) {
return merged;
}
elements.clear();
@@ -99,7 +99,7 @@ final class ProfilesParser {
break;
default:
Profiles value = equals(token);
if (context == Context.INVERT) {
if (context == Context.NEGATE) {
return value;
}
elements.add(value);
@@ -137,15 +137,14 @@ final class ProfilesParser {
return activeProfile -> activeProfile.test(profile);
}
private static Predicate<Profiles> isMatch(Predicate<String> activeProfile) {
return profiles -> profiles.matches(activeProfile);
private static Predicate<Profiles> isMatch(Predicate<String> activeProfiles) {
return profiles -> profiles.matches(activeProfiles);
}
private enum Operator {AND, OR}
private enum Operator { AND, OR }
private enum Context {NONE, INVERT, BRACKET}
private enum Context { NONE, NEGATE, PARENTHESIS }
private static class ParsedProfiles implements Profiles {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,13 +33,15 @@ import org.springframework.util.StringUtils;
*
* <p>Supports resolution as {@code java.io.File} if the class path
* resource resides in the file system, but not for resources in a JAR.
* Always supports resolution as URL.
* Always supports resolution as {@code java.net.URL}.
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 28.12.2003
* @see ClassLoader#getResourceAsStream(String)
* @see ClassLoader#getResource(String)
* @see Class#getResourceAsStream(String)
* @see Class#getResource(String)
*/
public class ClassPathResource extends AbstractFileResolvingResource {
@@ -124,7 +126,7 @@ public class ClassPathResource extends AbstractFileResolvingResource {
}
/**
* Return the ClassLoader that this resource will be obtained from.
* Return the {@link ClassLoader} that this resource will be obtained from.
*/
@Nullable
public final ClassLoader getClassLoader() {
@@ -134,8 +136,8 @@ public class ClassPathResource extends AbstractFileResolvingResource {
/**
* This implementation checks for the resolution of a resource URL.
* @see java.lang.ClassLoader#getResource(String)
* @see java.lang.Class#getResource(String)
* @see ClassLoader#getResource(String)
* @see Class#getResource(String)
*/
@Override
public boolean exists() {
@@ -145,8 +147,8 @@ public class ClassPathResource extends AbstractFileResolvingResource {
/**
* This implementation checks for the resolution of a resource URL upfront,
* then proceeding with {@link AbstractFileResolvingResource}'s length check.
* @see java.lang.ClassLoader#getResource(String)
* @see java.lang.Class#getResource(String)
* @see ClassLoader#getResource(String)
* @see Class#getResource(String)
*/
@Override
public boolean isReadable() {
@@ -179,9 +181,11 @@ public class ClassPathResource extends AbstractFileResolvingResource {
}
/**
* This implementation opens an InputStream for the given class path resource.
* @see java.lang.ClassLoader#getResourceAsStream(String)
* @see java.lang.Class#getResourceAsStream(String)
* This implementation opens an {@link InputStream} for the underlying class
* path resource, if available.
* @see ClassLoader#getResourceAsStream(String)
* @see Class#getResourceAsStream(String)
* @see ClassLoader#getSystemResourceAsStream(String)
*/
@Override
public InputStream getInputStream() throws IOException {
@@ -204,8 +208,8 @@ public class ClassPathResource extends AbstractFileResolvingResource {
/**
* This implementation returns a URL for the underlying class path resource,
* if available.
* @see java.lang.ClassLoader#getResource(String)
* @see java.lang.Class#getResource(String)
* @see ClassLoader#getResource(String)
* @see Class#getResource(String)
*/
@Override
public URL getURL() throws IOException {
@@ -217,9 +221,9 @@ public class ClassPathResource extends AbstractFileResolvingResource {
}
/**
* This implementation creates a ClassPathResource, applying the given path
* relative to the path of the underlying resource of this descriptor.
* @see org.springframework.util.StringUtils#applyRelativePath(String, String)
* This implementation creates a {@code ClassPathResource}, applying the given
* path relative to the path used to create this descriptor.
* @see StringUtils#applyRelativePath(String, String)
*/
@Override
public Resource createRelative(String relativePath) {
@@ -231,7 +235,7 @@ public class ClassPathResource extends AbstractFileResolvingResource {
/**
* This implementation returns the name of the file that this class path
* resource refers to.
* @see org.springframework.util.StringUtils#getFilename(String)
* @see StringUtils#getFilename(String)
*/
@Override
@Nullable
@@ -277,8 +281,7 @@ public class ClassPathResource extends AbstractFileResolvingResource {
}
/**
* This implementation returns the hash code of the underlying
* class path location.
* This implementation returns the hash code of the underlying class path location.
*/
@Override
public int hashCode() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -158,6 +158,7 @@ public class FileSystemResource extends AbstractResource implements WritableReso
/**
* This implementation returns whether the underlying file exists.
* @see java.io.File#exists()
* @see java.nio.file.Files#exists(Path, java.nio.file.LinkOption...)
*/
@Override
public boolean exists() {
@@ -169,6 +170,8 @@ public class FileSystemResource extends AbstractResource implements WritableReso
* (and corresponds to an actual file with content, not to a directory).
* @see java.io.File#canRead()
* @see java.io.File#isDirectory()
* @see java.nio.file.Files#isReadable(Path)
* @see java.nio.file.Files#isDirectory(Path, java.nio.file.LinkOption...)
*/
@Override
public boolean isReadable() {
@@ -177,8 +180,8 @@ public class FileSystemResource extends AbstractResource implements WritableReso
}
/**
* This implementation opens a NIO file stream for the underlying file.
* @see java.io.FileInputStream
* This implementation opens an NIO file stream for the underlying file.
* @see java.nio.file.Files#newInputStream(Path, java.nio.file.OpenOption...)
*/
@Override
public InputStream getInputStream() throws IOException {
@@ -195,6 +198,8 @@ public class FileSystemResource extends AbstractResource implements WritableReso
* (and corresponds to an actual file with content, not to a directory).
* @see java.io.File#canWrite()
* @see java.io.File#isDirectory()
* @see java.nio.file.Files#isWritable(Path)
* @see java.nio.file.Files#isDirectory(Path, java.nio.file.LinkOption...)
*/
@Override
public boolean isWritable() {
@@ -204,7 +209,7 @@ public class FileSystemResource extends AbstractResource implements WritableReso
/**
* This implementation opens a FileOutputStream for the underlying file.
* @see java.io.FileOutputStream
* @see java.nio.file.Files#newOutputStream(Path, java.nio.file.OpenOption...)
*/
@Override
public OutputStream getOutputStream() throws IOException {
@@ -214,6 +219,7 @@ public class FileSystemResource extends AbstractResource implements WritableReso
/**
* This implementation returns a URL for the underlying file.
* @see java.io.File#toURI()
* @see java.nio.file.Path#toUri()
*/
@Override
public URL getURL() throws IOException {
@@ -223,6 +229,7 @@ public class FileSystemResource extends AbstractResource implements WritableReso
/**
* This implementation returns a URI for the underlying file.
* @see java.io.File#toURI()
* @see java.nio.file.Path#toUri()
*/
@Override
public URI getURI() throws IOException {
@@ -324,6 +331,7 @@ public class FileSystemResource extends AbstractResource implements WritableReso
/**
* This implementation returns the name of the file.
* @see java.io.File#getName()
* @see java.nio.file.Path#getFileName()
*/
@Override
public String getFilename() {
@@ -334,6 +342,7 @@ public class FileSystemResource extends AbstractResource implements WritableReso
* This implementation returns a description that includes the absolute
* path of the file.
* @see java.io.File#getAbsolutePath()
* @see java.nio.file.Path#toAbsolutePath()
*/
@Override
public String getDescription() {
@@ -342,7 +351,7 @@ public class FileSystemResource extends AbstractResource implements WritableReso
/**
* This implementation compares the underlying File references.
* This implementation compares the underlying file paths.
*/
@Override
public boolean equals(@Nullable Object other) {
@@ -351,7 +360,7 @@ public class FileSystemResource extends AbstractResource implements WritableReso
}
/**
* This implementation returns the hash code of the underlying File reference.
* This implementation returns the hash code of the underlying file path.
*/
@Override
public int hashCode() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -61,7 +61,7 @@ public class PathResource extends AbstractResource implements WritableResource {
/**
* Create a new PathResource from a Path handle.
* Create a new {@code PathResource} from a {@link Path} handle.
* <p>Note: Unlike {@link FileSystemResource}, when building relative resources
* via {@link #createRelative}, the relative path will be built <i>underneath</i>
* the given root: e.g. Paths.get("C:/dir1/"), relative path "dir2" &rarr; "C:/dir1/dir2"!
@@ -73,7 +73,7 @@ public class PathResource extends AbstractResource implements WritableResource {
}
/**
* Create a new PathResource from a Path handle.
* Create a new {@code PathResource} from a path string.
* <p>Note: Unlike {@link FileSystemResource}, when building relative resources
* via {@link #createRelative}, the relative path will be built <i>underneath</i>
* the given root: e.g. Paths.get("C:/dir1/"), relative path "dir2" &rarr; "C:/dir1/dir2"!
@@ -86,7 +86,7 @@ public class PathResource extends AbstractResource implements WritableResource {
}
/**
* Create a new PathResource from a Path handle.
* Create a new {@code PathResource} from a {@link URI}.
* <p>Note: Unlike {@link FileSystemResource}, when building relative resources
* via {@link #createRelative}, the relative path will be built <i>underneath</i>
* the given root: e.g. Paths.get("C:/dir1/"), relative path "dir2" &rarr; "C:/dir1/dir2"!
@@ -127,7 +127,7 @@ public class PathResource extends AbstractResource implements WritableResource {
}
/**
* This implementation opens a InputStream for the underlying file.
* This implementation opens an {@link InputStream} for the underlying file.
* @see java.nio.file.spi.FileSystemProvider#newInputStream(Path, OpenOption...)
*/
@Override
@@ -153,7 +153,7 @@ public class PathResource extends AbstractResource implements WritableResource {
}
/**
* This implementation opens a OutputStream for the underlying file.
* This implementation opens an {@link OutputStream} for the underlying file.
* @see java.nio.file.spi.FileSystemProvider#newOutputStream(Path, OpenOption...)
*/
@Override
@@ -165,7 +165,7 @@ public class PathResource extends AbstractResource implements WritableResource {
}
/**
* This implementation returns a URL for the underlying file.
* This implementation returns a {@link URL} for the underlying file.
* @see java.nio.file.Path#toUri()
* @see java.net.URI#toURL()
*/
@@ -175,7 +175,7 @@ public class PathResource extends AbstractResource implements WritableResource {
}
/**
* This implementation returns a URI for the underlying file.
* This implementation returns a {@link URI} for the underlying file.
* @see java.nio.file.Path#toUri()
*/
@Override
@@ -192,7 +192,7 @@ public class PathResource extends AbstractResource implements WritableResource {
}
/**
* This implementation returns the underlying File reference.
* This implementation returns the underlying {@link File} reference.
*/
@Override
public File getFile() throws IOException {
@@ -207,7 +207,7 @@ public class PathResource extends AbstractResource implements WritableResource {
}
/**
* This implementation opens a Channel for the underlying file.
* This implementation opens a {@link ReadableByteChannel} for the underlying file.
* @see Files#newByteChannel(Path, OpenOption...)
*/
@Override
@@ -221,7 +221,7 @@ public class PathResource extends AbstractResource implements WritableResource {
}
/**
* This implementation opens a Channel for the underlying file.
* This implementation opens a {@link WritableByteChannel} for the underlying file.
* @see Files#newByteChannel(Path, OpenOption...)
*/
@Override
@@ -238,7 +238,7 @@ public class PathResource extends AbstractResource implements WritableResource {
}
/**
* This implementation returns the underlying File's timestamp.
* This implementation returns the underlying file's timestamp.
* @see java.nio.file.Files#getLastModifiedTime(Path, java.nio.file.LinkOption...)
*/
@Override
@@ -249,7 +249,7 @@ public class PathResource extends AbstractResource implements WritableResource {
}
/**
* This implementation creates a PathResource, applying the given path
* This implementation creates a {@link PathResource}, applying the given path
* relative to the path of the underlying file of this resource descriptor.
* @see java.nio.file.Path#resolve(String)
*/
@@ -274,7 +274,7 @@ public class PathResource extends AbstractResource implements WritableResource {
/**
* This implementation compares the underlying Path references.
* This implementation compares the underlying {@link Path} references.
*/
@Override
public boolean equals(@Nullable Object other) {
@@ -283,7 +283,7 @@ public class PathResource extends AbstractResource implements WritableResource {
}
/**
* This implementation returns the hash code of the underlying Path reference.
* This implementation returns the hash code of the underlying {@link Path} reference.
*/
@Override
public int hashCode() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@ public class DefaultDeserializer implements Deserializer<Object> {
/**
* Create a {@code DefaultDeserializer} for using an {@link ObjectInputStream}
* with the given {@code ClassLoader}.
* @param classLoader the ClassLoader to use
* @since 4.2.1
* @see ConfigurableObjectInputStream#ConfigurableObjectInputStream(InputStream, ClassLoader)
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import java.io.ByteArrayInputStream;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.serializer.DefaultDeserializer;
import org.springframework.core.serializer.Deserializer;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -50,10 +51,11 @@ public class DeserializingConverter implements Converter<byte[], Object> {
/**
* Create a {@code DeserializingConverter} for using an {@link java.io.ObjectInputStream}
* with the given {@code ClassLoader}.
* @param classLoader the ClassLoader to use
* @since 4.2.1
* @see DefaultDeserializer#DefaultDeserializer(ClassLoader)
*/
public DeserializingConverter(ClassLoader classLoader) {
public DeserializingConverter(@Nullable ClassLoader classLoader) {
this.deserializer = new DefaultDeserializer(classLoader);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -68,10 +68,10 @@ public abstract class FileSystemUtils {
}
/**
* Delete the supplied {@link File} &mdash; for directories,
* Delete the supplied {@link Path} &mdash; for directories,
* recursively delete any nested directories or files as well.
* @param root the root {@code File} to delete
* @return {@code true} if the {@code File} existed and was deleted,
* @param root the root {@code Path} to delete
* @return {@code true} if the {@code Path} existed and was deleted,
* or {@code false} if it did not exist
* @throws IOException in the case of I/O errors
* @since 5.0
@@ -16,17 +16,26 @@
package org.springframework.util;
import java.io.File;
import java.lang.reflect.Array;
import java.net.InetAddress;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.time.ZoneId;
import java.time.temporal.Temporal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Currency;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.TimeZone;
import java.util.UUID;
import java.util.regex.Pattern;
import org.springframework.lang.Nullable;
@@ -60,6 +69,9 @@ public abstract class ObjectUtils {
private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END;
private static final String ARRAY_ELEMENT_SEPARATOR = ", ";
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
private static final String NON_EMPTY_ARRAY = ARRAY_START + "..." + ARRAY_END;
private static final String EMPTY_COLLECTION = "[]";
private static final String NON_EMPTY_COLLECTION = "[...]";
/**
@@ -922,18 +934,31 @@ public abstract class ObjectUtils {
* <p>Returns:
* <ul>
* <li>{@code "null"} if {@code obj} is {@code null}</li>
* <li>{@code"Optional.empty"} if {@code obj} is an empty {@link Optional}</li>
* <li>{@code"Optional[<concise-string>]"} if {@code obj} is a non-empty {@code Optional},
* where {@code <concise-string>} is the result of invoking {@link #nullSafeConciseToString}
* on the object contained in the {@code Optional}</li>
* <li>{@code "{}"} if {@code obj} is an empty array or {@link Map}</li>
* <li>{@code "{...}"} if {@code obj} is a non-empty array or {@link Map}</li>
* <li>{@code "[]"} if {@code obj} is an empty {@link Collection}</li>
* <li>{@code "[...]"} if {@code obj} is a non-empty {@link Collection}</li>
* <li>{@linkplain Class#getName() Class name} if {@code obj} is a {@link Class}</li>
* <li>{@linkplain Charset#name() Charset name} if {@code obj} is a {@link Charset}</li>
* <li>{@linkplain TimeZone#getID() TimeZone ID} if {@code obj} is a {@link TimeZone}</li>
* <li>{@linkplain ZoneId#getId() Zone ID} if {@code obj} is a {@link ZoneId}</li>
* <li>Potentially {@linkplain StringUtils#truncate(CharSequence) truncated string}
* if {@code obj} is a {@link String} or {@link CharSequence}</li>
* <li>Potentially {@linkplain StringUtils#truncate(CharSequence) truncated string}
* if {@code obj} is a <em>simple type</em> whose {@code toString()} method returns
* a non-null value.</li>
* if {@code obj} is a <em>simple value type</em> whose {@code toString()} method
* returns a non-null value</li>
* <li>Otherwise, a string representation of the object's type name concatenated
* with {@code @} and a hex string form of the object's identity hash code</li>
* with {@code "@"} and a hex string form of the object's identity hash code</li>
* </ul>
* <p>In the context of this method, a <em>simple type</em> is any of the following:
* a primitive wrapper (excluding {@link Void}), an {@link Enum}, a {@link Number},
* a {@link Date}, a {@link Temporal}, a {@link URI}, a {@link URL}, or a {@link Locale}.
* <p>In the context of this method, a <em>simple value type</em> is any of the following:
* primitive wrapper (excluding {@link Void}), {@link Enum}, {@link Number},
* {@link Date}, {@link Temporal}, {@link File}, {@link Path}, {@link URI},
* {@link URL}, {@link InetAddress}, {@link Currency}, {@link Locale},
* {@link UUID}, {@link Pattern}.
* @param obj the object to build a string representation for
* @return a concise string representation of the supplied object
* @since 5.3.27
@@ -944,9 +969,33 @@ public abstract class ObjectUtils {
if (obj == null) {
return "null";
}
if (obj instanceof Optional<?>) {
Optional<?> optional = (Optional<?>) obj;
return (!optional.isPresent() ? "Optional.empty" :
String.format("Optional[%s]", nullSafeConciseToString(optional.get())));
}
if (obj.getClass().isArray()) {
return (Array.getLength(obj) == 0 ? EMPTY_ARRAY : NON_EMPTY_ARRAY);
}
if (obj instanceof Collection<?>) {
return (((Collection<?>) obj).isEmpty() ? EMPTY_COLLECTION : NON_EMPTY_COLLECTION);
}
if (obj instanceof Map<?, ?>) {
// EMPTY_ARRAY and NON_EMPTY_ARRAY are also used for maps.
return (((Map<?, ?>) obj).isEmpty() ? EMPTY_ARRAY : NON_EMPTY_ARRAY);
}
if (obj instanceof Class<?>) {
return ((Class<?>) obj).getName();
}
if (obj instanceof Charset) {
return ((Charset) obj).name();
}
if (obj instanceof TimeZone) {
return ((TimeZone) obj).getID();
}
if (obj instanceof ZoneId) {
return ((ZoneId) obj).getId();
}
if (obj instanceof CharSequence) {
return StringUtils.truncate((CharSequence) obj);
}
@@ -961,13 +1010,11 @@ public abstract class ObjectUtils {
}
/**
* Copy of {@link org.springframework.beans.BeanUtils#isSimpleValueType(Class)}.
* <p>Check if the given type represents a "simple" value type: a primitive or
* primitive wrapper, an enum, a String or other CharSequence, a Number, a
* Date, a Temporal, a URI, a URL, a Locale, or a Class.
* <p>{@code Void} and {@code void} are not considered simple value types.
* @param type the type to check
* @return whether the given type represents a "simple" value type
* Derived from {@link org.springframework.beans.BeanUtils#isSimpleValueType}.
* <p>As of 5.3.28, considering {@link UUID} in addition to the bean-level check.
* <p>As of 5.3.29, additionally considering {@link File}, {@link Path},
* {@link InetAddress}, {@link Charset}, {@link Currency}, {@link TimeZone},
* {@link ZoneId}, {@link Pattern}.
*/
private static boolean isSimpleValueType(Class<?> type) {
return (Void.class != type && void.class != type &&
@@ -977,9 +1024,18 @@ public abstract class ObjectUtils {
Number.class.isAssignableFrom(type) ||
Date.class.isAssignableFrom(type) ||
Temporal.class.isAssignableFrom(type) ||
ZoneId.class.isAssignableFrom(type) ||
TimeZone.class.isAssignableFrom(type) ||
File.class.isAssignableFrom(type) ||
Path.class.isAssignableFrom(type) ||
Charset.class.isAssignableFrom(type) ||
Currency.class.isAssignableFrom(type) ||
InetAddress.class.isAssignableFrom(type) ||
URI.class == type ||
URL.class == type ||
UUID.class == type ||
Locale.class == type ||
Pattern.class == type ||
Class.class == type));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -509,16 +509,8 @@ public abstract class ReflectionUtils {
* @see java.lang.Object#equals(Object)
*/
public static boolean isEqualsMethod(@Nullable Method method) {
if (method == null) {
return false;
}
if (method.getParameterCount() != 1) {
return false;
}
if (!method.getName().equals("equals")) {
return false;
}
return method.getParameterTypes()[0] == Object.class;
return (method != null && method.getParameterCount() == 1 && method.getName().equals("equals") &&
method.getParameterTypes()[0] == Object.class);
}
/**
@@ -526,7 +518,7 @@ public abstract class ReflectionUtils {
* @see java.lang.Object#hashCode()
*/
public static boolean isHashCodeMethod(@Nullable Method method) {
return method != null && method.getParameterCount() == 0 && method.getName().equals("hashCode");
return (method != null && method.getParameterCount() == 0 && method.getName().equals("hashCode"));
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -209,21 +209,23 @@ class CollectionFactoryTests {
@Test
void createsCollectionsCorrectly() {
// interfaces
assertThat(createCollection(List.class, 0)).isInstanceOf(ArrayList.class);
assertThat(createCollection(Set.class, 0)).isInstanceOf(LinkedHashSet.class);
assertThat(createCollection(Collection.class, 0)).isInstanceOf(LinkedHashSet.class);
assertThat(createCollection(SortedSet.class, 0)).isInstanceOf(TreeSet.class);
assertThat(createCollection(NavigableSet.class, 0)).isInstanceOf(TreeSet.class);
assertThat(createCollection(List.class, String.class, 0)).isInstanceOf(ArrayList.class);
assertThat(createCollection(Set.class, String.class, 0)).isInstanceOf(LinkedHashSet.class);
assertThat(createCollection(Collection.class, String.class, 0)).isInstanceOf(LinkedHashSet.class);
assertThat(createCollection(SortedSet.class, String.class, 0)).isInstanceOf(TreeSet.class);
assertThat(createCollection(NavigableSet.class, String.class, 0)).isInstanceOf(TreeSet.class);
testCollection(List.class, ArrayList.class);
testCollection(Set.class, LinkedHashSet.class);
testCollection(Collection.class, LinkedHashSet.class);
testCollection(SortedSet.class, TreeSet.class);
testCollection(NavigableSet.class, TreeSet.class);
// concrete types
assertThat(createCollection(HashSet.class, 0)).isInstanceOf(HashSet.class);
assertThat(createCollection(HashSet.class, String.class, 0)).isInstanceOf(HashSet.class);
testCollection(ArrayList.class, ArrayList.class);
testCollection(HashSet.class, HashSet.class);
testCollection(LinkedHashSet.class, LinkedHashSet.class);
testCollection(TreeSet.class, TreeSet.class);
}
private void testCollection(Class<?> collectionType, Class<?> resultType) {
assertThat(CollectionFactory.isApproximableCollectionType(collectionType)).isTrue();
assertThat(createCollection(collectionType, 0)).isExactlyInstanceOf(resultType);
assertThat(createCollection(collectionType, String.class, 0)).isExactlyInstanceOf(resultType);
}
@Test
@@ -258,20 +260,22 @@ class CollectionFactoryTests {
@Test
void createsMapsCorrectly() {
// interfaces
assertThat(createMap(Map.class, 0)).isInstanceOf(LinkedHashMap.class);
assertThat(createMap(SortedMap.class, 0)).isInstanceOf(TreeMap.class);
assertThat(createMap(NavigableMap.class, 0)).isInstanceOf(TreeMap.class);
assertThat(createMap(MultiValueMap.class, 0)).isInstanceOf(LinkedMultiValueMap.class);
assertThat(createMap(Map.class, String.class, 0)).isInstanceOf(LinkedHashMap.class);
assertThat(createMap(SortedMap.class, String.class, 0)).isInstanceOf(TreeMap.class);
assertThat(createMap(NavigableMap.class, String.class, 0)).isInstanceOf(TreeMap.class);
assertThat(createMap(MultiValueMap.class, String.class, 0)).isInstanceOf(LinkedMultiValueMap.class);
testMap(Map.class, LinkedHashMap.class);
testMap(SortedMap.class, TreeMap.class);
testMap(NavigableMap.class, TreeMap.class);
testMap(MultiValueMap.class, LinkedMultiValueMap.class);
// concrete types
assertThat(createMap(HashMap.class, 0)).isInstanceOf(HashMap.class);
testMap(HashMap.class, HashMap.class);
testMap(LinkedHashMap.class, LinkedHashMap.class);
testMap(TreeMap.class, TreeMap.class);
testMap(LinkedMultiValueMap.class, LinkedMultiValueMap.class);
}
assertThat(createMap(HashMap.class, String.class, 0)).isInstanceOf(HashMap.class);
private void testMap(Class<?> mapType, Class<?> resultType) {
assertThat(CollectionFactory.isApproximableMapType(mapType)).isTrue();
assertThat(createMap(mapType, 0)).isExactlyInstanceOf(resultType);
assertThat(createMap(mapType, String.class, 0)).isExactlyInstanceOf(resultType);
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -342,20 +342,31 @@ class DefaultConversionServiceTests {
@Test
void convertArrayToCollectionInterface() {
List<?> result = conversionService.convert(new String[] {"1", "2", "3"}, List.class);
assertThat(result.get(0)).isEqualTo("1");
assertThat(result.get(1)).isEqualTo("2");
assertThat(result.get(2)).isEqualTo("3");
@SuppressWarnings("unchecked")
Collection<String> result = conversionService.convert(new String[] {"1", "2", "3"}, Collection.class);
assertThat(result).isExactlyInstanceOf(LinkedHashSet.class).containsExactly("1", "2", "3");
}
@Test
void convertArrayToSetInterface() {
@SuppressWarnings("unchecked")
Collection<String> result = conversionService.convert(new String[] {"1", "2", "3"}, Set.class);
assertThat(result).isExactlyInstanceOf(LinkedHashSet.class).containsExactly("1", "2", "3");
}
@Test
void convertArrayToListInterface() {
@SuppressWarnings("unchecked")
List<String> result = conversionService.convert(new String[] {"1", "2", "3"}, List.class);
assertThat(result).isExactlyInstanceOf(ArrayList.class).containsExactly("1", "2", "3");
}
@Test
void convertArrayToCollectionGenericTypeConversion() throws Exception {
@SuppressWarnings("unchecked")
List<Integer> result = (List<Integer>) conversionService.convert(new String[] {"1", "2", "3"}, TypeDescriptor
.valueOf(String[].class), new TypeDescriptor(getClass().getDeclaredField("genericList")));
assertThat((int) result.get(0)).isEqualTo((int) Integer.valueOf(1));
assertThat((int) result.get(1)).isEqualTo((int) Integer.valueOf(2));
assertThat((int) result.get(2)).isEqualTo((int) Integer.valueOf(3));
List<Integer> result = (List<Integer>) conversionService.convert(new String[] {"1", "2", "3"},
TypeDescriptor.valueOf(String[].class), new TypeDescriptor(getClass().getDeclaredField("genericList")));
assertThat(result).isExactlyInstanceOf(ArrayList.class).containsExactly(1, 2, 3);
}
@Test
@@ -383,10 +394,9 @@ class DefaultConversionServiceTests {
@Test
void convertArrayToCollectionImpl() {
ArrayList<?> result = conversionService.convert(new String[] {"1", "2", "3"}, ArrayList.class);
assertThat(result.get(0)).isEqualTo("1");
assertThat(result.get(1)).isEqualTo("2");
assertThat(result.get(2)).isEqualTo("3");
@SuppressWarnings("unchecked")
ArrayList<String> result = conversionService.convert(new String[] {"1", "2", "3"}, ArrayList.class);
assertThat(result).isExactlyInstanceOf(ArrayList.class).containsExactly("1", "2", "3");
}
@Test
@@ -416,34 +426,25 @@ class DefaultConversionServiceTests {
@Test
void convertStringToArray() {
String[] result = conversionService.convert("1,2,3", String[].class);
assertThat(result.length).isEqualTo(3);
assertThat(result[0]).isEqualTo("1");
assertThat(result[1]).isEqualTo("2");
assertThat(result[2]).isEqualTo("3");
assertThat(result).containsExactly("1", "2", "3");
}
@Test
void convertStringToArrayWithElementConversion() {
Integer[] result = conversionService.convert("1,2,3", Integer[].class);
assertThat(result.length).isEqualTo(3);
assertThat((int) result[0]).isEqualTo((int) Integer.valueOf(1));
assertThat((int) result[1]).isEqualTo((int) Integer.valueOf(2));
assertThat((int) result[2]).isEqualTo((int) Integer.valueOf(3));
assertThat(result).containsExactly(1, 2, 3);
}
@Test
void convertStringToPrimitiveArrayWithElementConversion() {
int[] result = conversionService.convert("1,2,3", int[].class);
assertThat(result.length).isEqualTo(3);
assertThat(result[0]).isEqualTo(1);
assertThat(result[1]).isEqualTo(2);
assertThat(result[2]).isEqualTo(3);
assertThat(result).containsExactly(1, 2, 3);
}
@Test
void convertEmptyStringToArray() {
String[] result = conversionService.convert("", String[].class);
assertThat(result.length).isEqualTo(0);
assertThat(result).isEmpty();
}
@Test
@@ -457,7 +458,7 @@ class DefaultConversionServiceTests {
void convertArrayToObjectWithElementConversion() {
String[] array = new String[] {"3"};
Integer result = conversionService.convert(array, Integer.class);
assertThat((int) result).isEqualTo((int) Integer.valueOf(3));
assertThat(result).isEqualTo(3);
}
@Test
@@ -470,39 +471,27 @@ class DefaultConversionServiceTests {
@Test
void convertObjectToArray() {
Object[] result = conversionService.convert(3L, Object[].class);
assertThat(result.length).isEqualTo(1);
assertThat(result[0]).isEqualTo(3L);
assertThat(result).containsExactly(3L);
}
@Test
void convertObjectToArrayWithElementConversion() {
Integer[] result = conversionService.convert(3L, Integer[].class);
assertThat(result.length).isEqualTo(1);
assertThat((int) result[0]).isEqualTo((int) Integer.valueOf(3));
assertThat(result).containsExactly(3);
}
@Test
void convertCollectionToArray() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
List<String> list = Arrays.asList("1", "2", "3");
String[] result = conversionService.convert(list, String[].class);
assertThat(result[0]).isEqualTo("1");
assertThat(result[1]).isEqualTo("2");
assertThat(result[2]).isEqualTo("3");
assertThat(result).containsExactly("1", "2", "3");
}
@Test
void convertCollectionToArrayWithElementConversion() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
List<String> list = Arrays.asList("1", "2", "3");
Integer[] result = conversionService.convert(list, Integer[].class);
assertThat((int) result[0]).isEqualTo((int) Integer.valueOf(1));
assertThat((int) result[1]).isEqualTo((int) Integer.valueOf(2));
assertThat((int) result[2]).isEqualTo((int) Integer.valueOf(3));
assertThat(result).containsExactly(1, 2, 3);
}
@Test
@@ -522,34 +511,30 @@ class DefaultConversionServiceTests {
@Test
void convertStringToCollection() {
List<?> result = conversionService.convert("1,2,3", List.class);
assertThat(result.size()).isEqualTo(3);
assertThat(result.get(0)).isEqualTo("1");
assertThat(result.get(1)).isEqualTo("2");
assertThat(result.get(2)).isEqualTo("3");
@SuppressWarnings("unchecked")
List<String> result = conversionService.convert("1,2,3", List.class);
assertThat(result).containsExactly("1", "2", "3");
}
@Test
void convertStringToCollectionWithElementConversion() throws Exception {
List<?> result = (List<?>) conversionService.convert("1,2,3", TypeDescriptor.valueOf(String.class),
@SuppressWarnings("unchecked")
List<Integer> result = (List<Integer>) conversionService.convert("1,2,3", TypeDescriptor.valueOf(String.class),
new TypeDescriptor(getClass().getField("genericList")));
assertThat(result.size()).isEqualTo(3);
assertThat(result.get(0)).isEqualTo(1);
assertThat(result.get(1)).isEqualTo(2);
assertThat(result.get(2)).isEqualTo(3);
assertThat(result).containsExactly(1, 2, 3);
}
@Test
void convertEmptyStringToCollection() {
Collection<?> result = conversionService.convert("", Collection.class);
assertThat(result.size()).isEqualTo(0);
assertThat(result).isEmpty();
}
@Test
void convertCollectionToObject() {
List<Long> list = Collections.singletonList(3L);
Long result = conversionService.convert(list, Long.class);
assertThat(result).isEqualTo(Long.valueOf(3));
assertThat(result).isEqualTo(3);
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -40,30 +40,30 @@ class ProfilesTests {
@Test
void ofWhenNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() ->
Profiles.of((String[]) null))
.withMessageContaining("Must specify at least one profile");
assertThatIllegalArgumentException()
.isThrownBy(() -> Profiles.of((String[]) null))
.withMessage("Must specify at least one profile expression");
}
@Test
void ofWhenEmptyThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(Profiles::of)
.withMessageContaining("Must specify at least one profile");
.withMessage("Must specify at least one profile expression");
}
@Test
void ofNullElement() {
assertThatIllegalArgumentException().isThrownBy(() ->
Profiles.of((String) null))
.withMessageContaining("must contain text");
assertThatIllegalArgumentException()
.isThrownBy(() -> Profiles.of((String) null))
.withMessage("Invalid profile expression [null]: must contain text");
}
@Test
void ofEmptyElement() {
assertThatIllegalArgumentException().isThrownBy(() ->
Profiles.of(" "))
.withMessageContaining("must contain text");
assertThatIllegalArgumentException()
.isThrownBy(() -> Profiles.of(" "))
.withMessage("Invalid profile expression [ ]: must contain text");
}
@Test
@@ -356,7 +356,7 @@ class ProfilesTests {
private static void assertMalformed(Supplier<Profiles> supplier) {
assertThatIllegalArgumentException()
.isThrownBy(supplier::get)
.withMessageContaining("Malformed");
.withMessageStartingWith("Malformed profile expression");
}
private static Predicate<String> activeProfiles(String... profiles) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,9 +18,9 @@ package org.springframework.core.env;
import java.security.AccessControlException;
import java.security.Permission;
import java.util.Arrays;
import java.util.Map;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.core.SpringProperties;
@@ -40,8 +40,7 @@ import static org.springframework.core.env.AbstractEnvironment.RESERVED_DEFAULT_
* @author Juergen Hoeller
* @author Sam Brannen
*/
@SuppressWarnings("deprecation")
public class StandardEnvironmentTests {
class StandardEnvironmentTests {
private static final String ALLOWED_PROPERTY_NAME = "theanswer";
private static final String ALLOWED_PROPERTY_VALUE = "42";
@@ -81,8 +80,8 @@ public class StandardEnvironmentTests {
assertThat(parent.getProperty("parentKey")).isEqualTo("parentVal");
assertThat(parent.getProperty("bothKey")).isEqualTo("parentBothVal");
assertThat(child.getActiveProfiles()).isEqualTo(new String[]{"c1","c2"});
assertThat(parent.getActiveProfiles()).isEqualTo(new String[]{"p1","p2"});
assertThat(child.getActiveProfiles()).containsExactly("c1", "c2");
assertThat(parent.getActiveProfiles()).containsExactly("p1", "p2");
child.merge(parent);
@@ -94,8 +93,8 @@ public class StandardEnvironmentTests {
assertThat(parent.getProperty("parentKey")).isEqualTo("parentVal");
assertThat(parent.getProperty("bothKey")).isEqualTo("parentBothVal");
assertThat(child.getActiveProfiles()).isEqualTo(new String[]{"c1","c2","p1","p2"});
assertThat(parent.getActiveProfiles()).isEqualTo(new String[]{"p1","p2"});
assertThat(child.getActiveProfiles()).containsExactly("c1", "c2", "p1", "p2");
assertThat(parent.getActiveProfiles()).containsExactly("p1", "p2");
}
@Test
@@ -121,16 +120,14 @@ public class StandardEnvironmentTests {
@Test
void defaultProfilesContainsDefaultProfileByDefault() {
assertThat(environment.getDefaultProfiles().length).isEqualTo(1);
assertThat(environment.getDefaultProfiles()[0]).isEqualTo("default");
assertThat(environment.getDefaultProfiles()).containsExactly("default");
}
@Test
void setActiveProfiles() {
environment.setActiveProfiles("local", "embedded");
String[] activeProfiles = environment.getActiveProfiles();
assertThat(activeProfiles).contains("local", "embedded");
assertThat(activeProfiles.length).isEqualTo(2);
assertThat(activeProfiles).containsExactly("local", "embedded");
}
@Test
@@ -177,15 +174,12 @@ public class StandardEnvironmentTests {
void addActiveProfile() {
assertThat(environment.getActiveProfiles().length).isEqualTo(0);
environment.setActiveProfiles("local", "embedded");
assertThat(environment.getActiveProfiles()).contains("local", "embedded");
assertThat(environment.getActiveProfiles().length).isEqualTo(2);
assertThat(environment.getActiveProfiles()).containsExactly("local", "embedded");
environment.addActiveProfile("p1");
assertThat(environment.getActiveProfiles()).contains("p1");
assertThat(environment.getActiveProfiles().length).isEqualTo(3);
assertThat(environment.getActiveProfiles()).containsExactly("local", "embedded", "p1");
environment.addActiveProfile("p2");
environment.addActiveProfile("p3");
assertThat(environment.getActiveProfiles()).contains("p2", "p3");
assertThat(environment.getActiveProfiles().length).isEqualTo(5);
assertThat(environment.getActiveProfiles()).containsExactly("local", "embedded", "p1", "p2", "p3");
}
@Test
@@ -195,24 +189,17 @@ public class StandardEnvironmentTests {
env.getPropertySources().addFirst(new MockPropertySource().withProperty(ACTIVE_PROFILES_PROPERTY_NAME, "p1"));
assertThat(env.getProperty(ACTIVE_PROFILES_PROPERTY_NAME)).isEqualTo("p1");
env.addActiveProfile("p2");
assertThat(env.getActiveProfiles()).contains("p1", "p2");
assertThat(env.getActiveProfiles()).containsExactly("p1", "p2");
}
@Test
void reservedDefaultProfile() {
assertThat(environment.getDefaultProfiles()).isEqualTo(new String[]{RESERVED_DEFAULT_PROFILE_NAME});
System.setProperty(DEFAULT_PROFILES_PROPERTY_NAME, "d0");
assertThat(environment.getDefaultProfiles()).isEqualTo(new String[]{"d0"});
environment.setDefaultProfiles("d1", "d2");
assertThat(environment.getDefaultProfiles()).isEqualTo(new String[]{"d1","d2"});
System.clearProperty(DEFAULT_PROFILES_PROPERTY_NAME);
}
@Test
void defaultProfileWithCircularPlaceholder() {
assertThat(environment.getDefaultProfiles()).containsExactly(RESERVED_DEFAULT_PROFILE_NAME);
try {
System.setProperty(DEFAULT_PROFILES_PROPERTY_NAME, "${spring.profiles.default}");
assertThatIllegalArgumentException().isThrownBy(environment::getDefaultProfiles);
System.setProperty(DEFAULT_PROFILES_PROPERTY_NAME, "d0");
assertThat(environment.getDefaultProfiles()).containsExactly("d0");
environment.setDefaultProfiles("d1", "d2");
assertThat(environment.getDefaultProfiles()).containsExactly("d1","d2");
}
finally {
System.clearProperty(DEFAULT_PROFILES_PROPERTY_NAME);
@@ -220,40 +207,23 @@ public class StandardEnvironmentTests {
}
@Test
void getActiveProfiles_systemPropertiesEmpty() {
assertThat(environment.getActiveProfiles().length).isEqualTo(0);
System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "");
assertThat(environment.getActiveProfiles().length).isEqualTo(0);
System.clearProperty(ACTIVE_PROFILES_PROPERTY_NAME);
}
@Test
void getActiveProfiles_fromSystemProperties() {
System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "foo");
assertThat(Arrays.asList(environment.getActiveProfiles())).contains("foo");
System.clearProperty(ACTIVE_PROFILES_PROPERTY_NAME);
}
@Test
void getActiveProfiles_fromSystemProperties_withMultipleProfiles() {
System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "foo,bar");
assertThat(environment.getActiveProfiles()).contains("foo", "bar");
System.clearProperty(ACTIVE_PROFILES_PROPERTY_NAME);
}
@Test
void getActiveProfiles_fromSystemProperties_withMultipleProfiles_withWhitespace() {
System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, " bar , baz "); // notice whitespace
assertThat(environment.getActiveProfiles()).contains("bar", "baz");
System.clearProperty(ACTIVE_PROFILES_PROPERTY_NAME);
void defaultProfileWithCircularPlaceholder() {
try {
System.setProperty(DEFAULT_PROFILES_PROPERTY_NAME, "${spring.profiles.default}");
assertThatIllegalArgumentException()
.isThrownBy(environment::getDefaultProfiles)
.withMessage("Circular placeholder reference 'spring.profiles.default' in property definitions");
}
finally {
System.clearProperty(DEFAULT_PROFILES_PROPERTY_NAME);
}
}
@Test
void getDefaultProfiles() {
assertThat(environment.getDefaultProfiles()).isEqualTo(new String[] {RESERVED_DEFAULT_PROFILE_NAME});
assertThat(environment.getDefaultProfiles()).containsExactly(RESERVED_DEFAULT_PROFILE_NAME);
environment.getPropertySources().addFirst(new MockPropertySource().withProperty(DEFAULT_PROFILES_PROPERTY_NAME, "pd1"));
assertThat(environment.getDefaultProfiles().length).isEqualTo(1);
assertThat(Arrays.asList(environment.getDefaultProfiles())).contains("pd1");
assertThat(environment.getDefaultProfiles()).containsExactly("pd1");
}
@Test
@@ -261,82 +231,9 @@ public class StandardEnvironmentTests {
environment.setDefaultProfiles();
assertThat(environment.getDefaultProfiles().length).isEqualTo(0);
environment.setDefaultProfiles("pd1");
assertThat(Arrays.asList(environment.getDefaultProfiles())).contains("pd1");
assertThat(environment.getDefaultProfiles()).containsExactly("pd1");
environment.setDefaultProfiles("pd2", "pd3");
assertThat(environment.getDefaultProfiles()).doesNotContain("pd1");
assertThat(environment.getDefaultProfiles()).contains("pd2", "pd3");
}
@Test
void acceptsProfiles_withEmptyArgumentList() {
assertThatIllegalArgumentException().isThrownBy(
environment::acceptsProfiles);
}
@Test
void acceptsProfiles_withNullArgumentList() {
assertThatIllegalArgumentException().isThrownBy(() -> environment.acceptsProfiles((String[]) null));
}
@Test
void acceptsProfiles_withNullArgument() {
assertThatIllegalArgumentException().isThrownBy(() -> environment.acceptsProfiles((String) null));
}
@Test
void acceptsProfiles_withEmptyArgument() {
assertThatIllegalArgumentException().isThrownBy(() -> environment.acceptsProfiles(""));
}
@Test
void acceptsProfiles_activeProfileSetProgrammatically() {
assertThat(environment.acceptsProfiles("p1", "p2")).isFalse();
environment.setActiveProfiles("p1");
assertThat(environment.acceptsProfiles("p1", "p2")).isTrue();
environment.setActiveProfiles("p2");
assertThat(environment.acceptsProfiles("p1", "p2")).isTrue();
environment.setActiveProfiles("p1", "p2");
assertThat(environment.acceptsProfiles("p1", "p2")).isTrue();
}
@Test
void acceptsProfiles_activeProfileSetViaProperty() {
assertThat(environment.acceptsProfiles("p1")).isFalse();
environment.getPropertySources().addFirst(new MockPropertySource().withProperty(ACTIVE_PROFILES_PROPERTY_NAME, "p1"));
assertThat(environment.acceptsProfiles("p1")).isTrue();
}
@Test
void acceptsProfiles_defaultProfile() {
assertThat(environment.acceptsProfiles("pd")).isFalse();
environment.setDefaultProfiles("pd");
assertThat(environment.acceptsProfiles("pd")).isTrue();
environment.setActiveProfiles("p1");
assertThat(environment.acceptsProfiles("pd")).isFalse();
assertThat(environment.acceptsProfiles("p1")).isTrue();
}
@Test
void acceptsProfiles_withNotOperator() {
assertThat(environment.acceptsProfiles("p1")).isFalse();
assertThat(environment.acceptsProfiles("!p1")).isTrue();
environment.addActiveProfile("p1");
assertThat(environment.acceptsProfiles("p1")).isTrue();
assertThat(environment.acceptsProfiles("!p1")).isFalse();
}
@Test
void acceptsProfiles_withInvalidNotOperator() {
assertThatIllegalArgumentException().isThrownBy(() -> environment.acceptsProfiles("p1", "!"));
}
@Test
void acceptsProfiles_withProfileExpression() {
assertThat(environment.acceptsProfiles(Profiles.of("p1 & p2"))).isFalse();
environment.addActiveProfile("p1");
assertThat(environment.acceptsProfiles(Profiles.of("p1 & p2"))).isFalse();
environment.addActiveProfile("p2");
assertThat(environment.acceptsProfiles(Profiles.of("p1 & p2"))).isTrue();
assertThat(environment.getDefaultProfiles()).containsExactly("pd2", "pd3");
}
@Test
@@ -346,88 +243,99 @@ public class StandardEnvironmentTests {
protected void validateProfile(String profile) {
super.validateProfile(profile);
if (profile.contains("-")) {
throw new IllegalArgumentException(
"Invalid profile [" + profile + "]: must not contain dash character");
throw new IllegalArgumentException("Invalid profile [" + profile + "]: must not contain dash character");
}
}
};
env.addActiveProfile("validProfile"); // succeeds
assertThatIllegalArgumentException().isThrownBy(() ->
env.addActiveProfile("invalid-profile"))
assertThatIllegalArgumentException()
.isThrownBy(() -> env.addActiveProfile("invalid-profile"))
.withMessage("Invalid profile [invalid-profile]: must not contain dash character");
}
@Test
void suppressGetenvAccessThroughSystemProperty() {
System.setProperty("spring.getenv.ignore", "true");
assertThat(environment.getSystemEnvironment().isEmpty()).isTrue();
System.clearProperty("spring.getenv.ignore");
try {
System.setProperty("spring.getenv.ignore", "true");
assertThat(environment.getSystemEnvironment()).isEmpty();
}
finally {
System.clearProperty("spring.getenv.ignore");
}
}
@Test
void suppressGetenvAccessThroughSpringProperty() {
SpringProperties.setProperty("spring.getenv.ignore", "true");
assertThat(environment.getSystemEnvironment().isEmpty()).isTrue();
SpringProperties.setProperty("spring.getenv.ignore", null);
try {
SpringProperties.setProperty("spring.getenv.ignore", "true");
assertThat(environment.getSystemEnvironment()).isEmpty();
}
finally {
SpringProperties.setProperty("spring.getenv.ignore", null);
}
}
@Test
void suppressGetenvAccessThroughSpringFlag() {
SpringProperties.setFlag("spring.getenv.ignore");
assertThat(environment.getSystemEnvironment().isEmpty()).isTrue();
SpringProperties.setProperty("spring.getenv.ignore", null);
try {
SpringProperties.setFlag("spring.getenv.ignore");
assertThat(environment.getSystemEnvironment()).isEmpty();
}
finally {
SpringProperties.setProperty("spring.getenv.ignore", null);
}
}
@Test
void getSystemProperties_withAndWithoutSecurityManager() {
System.setProperty(ALLOWED_PROPERTY_NAME, ALLOWED_PROPERTY_VALUE);
System.setProperty(DISALLOWED_PROPERTY_NAME, DISALLOWED_PROPERTY_VALUE);
System.getProperties().put(STRING_PROPERTY_NAME, NON_STRING_PROPERTY_VALUE);
System.getProperties().put(NON_STRING_PROPERTY_NAME, STRING_PROPERTY_VALUE);
{
Map<?, ?> systemProperties = environment.getSystemProperties();
assertThat(systemProperties).isNotNull();
assertThat(System.getProperties()).isSameAs(systemProperties);
assertThat(systemProperties.get(ALLOWED_PROPERTY_NAME)).isEqualTo(ALLOWED_PROPERTY_VALUE);
assertThat(systemProperties.get(DISALLOWED_PROPERTY_NAME)).isEqualTo(DISALLOWED_PROPERTY_VALUE);
// non-string keys and values work fine... until the security manager is introduced below
assertThat(systemProperties.get(STRING_PROPERTY_NAME)).isEqualTo(NON_STRING_PROPERTY_VALUE);
assertThat(systemProperties.get(NON_STRING_PROPERTY_NAME)).isEqualTo(STRING_PROPERTY_VALUE);
}
SecurityManager oldSecurityManager = System.getSecurityManager();
SecurityManager securityManager = new SecurityManager() {
@Override
public void checkPropertiesAccess() {
// see https://download.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#getProperties()
throw new AccessControlException("Accessing the system properties is disallowed");
}
@Override
public void checkPropertyAccess(String key) {
// see https://download.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#getProperty(java.lang.String)
if (DISALLOWED_PROPERTY_NAME.equals(key)) {
throw new AccessControlException(
String.format("Accessing the system property [%s] is disallowed", DISALLOWED_PROPERTY_NAME));
}
}
@Override
public void checkPermission(Permission perm) {
// allow everything else
}
};
SecurityManager originalSecurityManager = System.getSecurityManager();
try {
System.setProperty(ALLOWED_PROPERTY_NAME, ALLOWED_PROPERTY_VALUE);
System.setProperty(DISALLOWED_PROPERTY_NAME, DISALLOWED_PROPERTY_VALUE);
System.getProperties().put(STRING_PROPERTY_NAME, NON_STRING_PROPERTY_VALUE);
System.getProperties().put(NON_STRING_PROPERTY_NAME, STRING_PROPERTY_VALUE);
{
Map<?, ?> systemProperties = environment.getSystemProperties();
assertThat(systemProperties).isNotNull();
assertThat(System.getProperties()).isSameAs(systemProperties);
assertThat(systemProperties.get(ALLOWED_PROPERTY_NAME)).isEqualTo(ALLOWED_PROPERTY_VALUE);
assertThat(systemProperties.get(DISALLOWED_PROPERTY_NAME)).isEqualTo(DISALLOWED_PROPERTY_VALUE);
// non-string keys and values work fine... until the security manager is introduced below
assertThat(systemProperties.get(STRING_PROPERTY_NAME)).isEqualTo(NON_STRING_PROPERTY_VALUE);
assertThat(systemProperties.get(NON_STRING_PROPERTY_NAME)).isEqualTo(STRING_PROPERTY_VALUE);
}
SecurityManager securityManager = new SecurityManager() {
@Override
public void checkPropertiesAccess() {
// see https://download.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#getProperties()
throw new AccessControlException("Accessing the system properties is disallowed");
}
@Override
public void checkPropertyAccess(String key) {
// see https://download.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#getProperty(java.lang.String)
if (DISALLOWED_PROPERTY_NAME.equals(key)) {
throw new AccessControlException(
String.format("Accessing the system property [%s] is disallowed", DISALLOWED_PROPERTY_NAME));
}
}
@Override
public void checkPermission(Permission perm) {
// allow everything else
}
};
System.setSecurityManager(securityManager);
{
Map<?, ?> systemProperties = environment.getSystemProperties();
assertThat(systemProperties).isNotNull();
assertThat(systemProperties).isInstanceOf(ReadOnlySystemAttributesMap.class);
assertThat((String)systemProperties.get(ALLOWED_PROPERTY_NAME)).isEqualTo(ALLOWED_PROPERTY_VALUE);
assertThat(systemProperties.get(ALLOWED_PROPERTY_NAME)).isEqualTo(ALLOWED_PROPERTY_VALUE);
assertThat(systemProperties.get(DISALLOWED_PROPERTY_NAME)).isNull();
// nothing we can do here in terms of warning the user that there was
@@ -441,12 +349,12 @@ public class StandardEnvironmentTests {
// the user that under these very special conditions (non-object key +
// SecurityManager that disallows access to system properties), they
// cannot do what they're attempting.
assertThatIllegalArgumentException().as("searching with non-string key against ReadOnlySystemAttributesMap").isThrownBy(() ->
systemProperties.get(NON_STRING_PROPERTY_NAME));
assertThatIllegalArgumentException().as("searching with non-string key against ReadOnlySystemAttributesMap")
.isThrownBy(() -> systemProperties.get(NON_STRING_PROPERTY_NAME));
}
}
finally {
System.setSecurityManager(oldSecurityManager);
System.setSecurityManager(originalSecurityManager);
System.clearProperty(ALLOWED_PROPERTY_NAME);
System.clearProperty(DISALLOWED_PROPERTY_NAME);
System.getProperties().remove(STRING_PROPERTY_NAME);
@@ -499,4 +407,250 @@ public class StandardEnvironmentTests {
EnvironmentTestUtils.getModifiableSystemEnvironment().remove(DISALLOWED_PROPERTY_NAME);
}
@Nested
class GetActiveProfiles {
@Test
void systemPropertiesEmpty() {
assertThat(environment.getActiveProfiles()).isEmpty();
try {
System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "");
assertThat(environment.getActiveProfiles()).isEmpty();
}
finally {
System.clearProperty(ACTIVE_PROFILES_PROPERTY_NAME);
}
}
@Test
void fromSystemProperties() {
try {
System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "foo");
assertThat(environment.getActiveProfiles()).containsExactly("foo");
}
finally {
System.clearProperty(ACTIVE_PROFILES_PROPERTY_NAME);
}
}
@Test
void fromSystemProperties_withMultipleProfiles() {
try {
System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, "foo,bar");
assertThat(environment.getActiveProfiles()).containsExactly("foo", "bar");
}
finally {
System.clearProperty(ACTIVE_PROFILES_PROPERTY_NAME);
}
}
@Test
void fromSystemProperties_withMultipleProfiles_withWhitespace() {
try {
System.setProperty(ACTIVE_PROFILES_PROPERTY_NAME, " bar , baz "); // notice whitespace
assertThat(environment.getActiveProfiles()).containsExactly("bar", "baz");
}
finally {
System.clearProperty(ACTIVE_PROFILES_PROPERTY_NAME);
}
}
}
@Nested
class AcceptsProfilesTests {
@Test
@SuppressWarnings("deprecation")
void withEmptyArgumentList() {
assertThatIllegalArgumentException().isThrownBy(environment::acceptsProfiles);
}
@Test
@SuppressWarnings("deprecation")
void withNullArgumentList() {
assertThatIllegalArgumentException().isThrownBy(() -> environment.acceptsProfiles((String[]) null));
}
@Test
@SuppressWarnings("deprecation")
void withNullArgument() {
assertThatIllegalArgumentException().isThrownBy(() -> environment.acceptsProfiles((String) null));
}
@Test
@SuppressWarnings("deprecation")
void withEmptyArgument() {
assertThatIllegalArgumentException().isThrownBy(() -> environment.acceptsProfiles(""));
}
@Test
@SuppressWarnings("deprecation")
void activeProfileSetProgrammatically() {
assertThat(environment.acceptsProfiles("p1", "p2")).isFalse();
environment.setActiveProfiles("p1");
assertThat(environment.acceptsProfiles("p1", "p2")).isTrue();
environment.setActiveProfiles("p2");
assertThat(environment.acceptsProfiles("p1", "p2")).isTrue();
environment.setActiveProfiles("p1", "p2");
assertThat(environment.acceptsProfiles("p1", "p2")).isTrue();
}
@Test
@SuppressWarnings("deprecation")
void activeProfileSetViaProperty() {
assertThat(environment.acceptsProfiles("p1")).isFalse();
environment.getPropertySources().addFirst(new MockPropertySource().withProperty(ACTIVE_PROFILES_PROPERTY_NAME, "p1"));
assertThat(environment.acceptsProfiles("p1")).isTrue();
}
@Test
@SuppressWarnings("deprecation")
void defaultProfile() {
assertThat(environment.acceptsProfiles("pd")).isFalse();
environment.setDefaultProfiles("pd");
assertThat(environment.acceptsProfiles("pd")).isTrue();
environment.setActiveProfiles("p1");
assertThat(environment.acceptsProfiles("pd")).isFalse();
assertThat(environment.acceptsProfiles("p1")).isTrue();
}
@Test
@SuppressWarnings("deprecation")
void withNotOperator() {
assertThat(environment.acceptsProfiles("p1")).isFalse();
assertThat(environment.acceptsProfiles("!p1")).isTrue();
environment.addActiveProfile("p1");
assertThat(environment.acceptsProfiles("p1")).isTrue();
assertThat(environment.acceptsProfiles("!p1")).isFalse();
}
@Test
@SuppressWarnings("deprecation")
void withInvalidNotOperator() {
assertThatIllegalArgumentException().isThrownBy(() -> environment.acceptsProfiles("p1", "!"));
}
@Test
void withProfileExpression() {
assertThat(environment.acceptsProfiles(Profiles.of("p1 & p2"))).isFalse();
environment.addActiveProfile("p1");
assertThat(environment.acceptsProfiles(Profiles.of("p1 & p2"))).isFalse();
environment.addActiveProfile("p2");
assertThat(environment.acceptsProfiles(Profiles.of("p1 & p2"))).isTrue();
}
}
@Nested
class MatchesProfilesTests {
@Test
@SuppressWarnings("deprecation")
void withEmptyArgumentList() {
assertThatIllegalArgumentException().isThrownBy(environment::matchesProfiles);
}
@Test
@SuppressWarnings("deprecation")
void withNullArgumentList() {
assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles((String[]) null));
}
@Test
@SuppressWarnings("deprecation")
void withNullArgument() {
assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles((String) null));
assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("p1", null));
}
@Test
@SuppressWarnings("deprecation")
void withEmptyArgument() {
assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles(""));
assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("p1", ""));
assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("p1", " "));
}
@Test
@SuppressWarnings("deprecation")
void withInvalidNotOperator() {
assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("p1", "!"));
}
@Test
@SuppressWarnings("deprecation")
void withInvalidCompoundExpressionGrouping() {
assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("p1 | p2 & p3"));
assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("p1 & p2 | p3"));
assertThatIllegalArgumentException().isThrownBy(() -> environment.matchesProfiles("p1 & (p2 | p3) | p4"));
}
@Test
@SuppressWarnings("deprecation")
void activeProfileSetProgrammatically() {
assertThat(environment.matchesProfiles("p1", "p2")).isFalse();
environment.setActiveProfiles("p1");
assertThat(environment.matchesProfiles("p1", "p2")).isTrue();
environment.setActiveProfiles("p2");
assertThat(environment.matchesProfiles("p1", "p2")).isTrue();
environment.setActiveProfiles("p1", "p2");
assertThat(environment.matchesProfiles("p1", "p2")).isTrue();
}
@Test
@SuppressWarnings("deprecation")
void activeProfileSetViaProperty() {
assertThat(environment.matchesProfiles("p1")).isFalse();
environment.getPropertySources().addFirst(new MockPropertySource().withProperty(ACTIVE_PROFILES_PROPERTY_NAME, "p1"));
assertThat(environment.matchesProfiles("p1")).isTrue();
}
@Test
@SuppressWarnings("deprecation")
void defaultProfile() {
assertThat(environment.matchesProfiles("pd")).isFalse();
environment.setDefaultProfiles("pd");
assertThat(environment.matchesProfiles("pd")).isTrue();
environment.setActiveProfiles("p1");
assertThat(environment.matchesProfiles("pd")).isFalse();
assertThat(environment.matchesProfiles("p1")).isTrue();
}
@Test
@SuppressWarnings("deprecation")
void withNotOperator() {
assertThat(environment.matchesProfiles("p1")).isFalse();
assertThat(environment.matchesProfiles("!p1")).isTrue();
environment.addActiveProfile("p1");
assertThat(environment.matchesProfiles("p1")).isTrue();
assertThat(environment.matchesProfiles("!p1")).isFalse();
}
@Test
void withProfileExpressions() {
assertThat(environment.matchesProfiles("p1 & p2")).isFalse();
environment.addActiveProfile("p1");
assertThat(environment.matchesProfiles("p1 | p2")).isTrue();
assertThat(environment.matchesProfiles("p1 & p2")).isFalse();
environment.addActiveProfile("p2");
assertThat(environment.matchesProfiles("p1 & p2")).isTrue();
assertThat(environment.matchesProfiles("p1 | p2")).isTrue();
assertThat(environment.matchesProfiles("foo | p1", "p2")).isTrue();
assertThat(environment.matchesProfiles("foo | p2", "p1")).isTrue();
assertThat(environment.matchesProfiles("foo | (p2 & p1)")).isTrue();
assertThat(environment.matchesProfiles("p2 & (foo | p1)")).isTrue();
assertThat(environment.matchesProfiles("foo", "(p2 & p1)")).isTrue();
}
}
}
@@ -16,20 +16,34 @@
package org.springframework.util;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.URI;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Collections;
import java.util.Currency;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.regex.Pattern;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -223,7 +237,7 @@ class ObjectUtilsTests {
}
@Test
void addObjectToNullArray() throws Exception {
void addObjectToNullArray() {
String newElement = "foo";
String[] newArray = ObjectUtils.addObjectToArray(null, newElement);
assertThat(newArray).hasSize(1);
@@ -231,14 +245,14 @@ class ObjectUtilsTests {
}
@Test
void addNullObjectToNullArray() throws Exception {
void addNullObjectToNullArray() {
Object[] newArray = ObjectUtils.addObjectToArray(null, null);
assertThat(newArray).hasSize(1);
assertThat(newArray[0]).isNull();
}
@Test
void nullSafeEqualsWithArrays() throws Exception {
void nullSafeEqualsWithArrays() {
assertThat(ObjectUtils.nullSafeEquals(new String[] {"a", "b", "c"}, new String[] {"a", "b", "c"})).isTrue();
assertThat(ObjectUtils.nullSafeEquals(new int[] {1, 2, 3}, new int[] {1, 2, 3})).isTrue();
}
@@ -826,6 +840,17 @@ class ObjectUtilsTests {
.withMessage("Constant [bogus] does not exist in enum type org.springframework.util.ObjectUtilsTests$Tropes");
}
private static void assertEqualHashCodes(int expected, Object array) {
int actual = ObjectUtils.nullSafeHashCode(array);
assertThat(actual).isEqualTo(expected);
assertThat(array.hashCode()).isNotEqualTo(actual);
}
enum Tropes {FOO, BAR, baz}
@Nested
class NullSafeConciseToStringTests {
@@ -837,6 +862,41 @@ class ObjectUtilsTests {
assertThat(ObjectUtils.nullSafeConciseToString(null)).isEqualTo("null");
}
@Test
void nullSafeConciseToStringForEmptyOptional() {
Optional<String> optional = Optional.empty();
assertThat(ObjectUtils.nullSafeConciseToString(optional)).isEqualTo("Optional.empty");
}
@Test
void nullSafeConciseToStringForNonEmptyOptionals() {
Optional<Tropes> optionalEnum = Optional.of(Tropes.BAR);
String expected = "Optional[BAR]";
assertThat(ObjectUtils.nullSafeConciseToString(optionalEnum)).isEqualTo(expected);
String repeat100 = repeat("X", 100);
String repeat101 = repeat("X", 101);
Optional<String> optionalString = Optional.of(repeat100);
expected = String.format("Optional[%s]", repeat100);
assertThat(ObjectUtils.nullSafeConciseToString(optionalString)).isEqualTo(expected);
optionalString = Optional.of(repeat101);
expected = String.format("Optional[%s]", repeat100 + truncated);
assertThat(ObjectUtils.nullSafeConciseToString(optionalString)).isEqualTo(expected);
}
@Test
void nullSafeConciseToStringForNonEmptyOptionalCustomType() {
class CustomType {
}
CustomType customType = new CustomType();
Optional<CustomType> optional = Optional.of(customType);
String expected = String.format("Optional[%s]", ObjectUtils.nullSafeConciseToString(customType));
assertThat(ObjectUtils.nullSafeConciseToString(optional)).isEqualTo(expected);
}
@Test
void nullSafeConciseToStringForClass() {
assertThat(ObjectUtils.nullSafeConciseToString(String.class)).isEqualTo("java.lang.String");
@@ -869,11 +929,19 @@ class ObjectUtilsTests {
}
@Test
void nullSafeConciseToStringForNumber() {
void nullSafeConciseToStringForPrimitivesAndWrappers() {
assertThat(ObjectUtils.nullSafeConciseToString(true)).isEqualTo("true");
assertThat(ObjectUtils.nullSafeConciseToString('X')).isEqualTo("X");
assertThat(ObjectUtils.nullSafeConciseToString(42L)).isEqualTo("42");
assertThat(ObjectUtils.nullSafeConciseToString(99.1234D)).isEqualTo("99.1234");
}
@Test
void nullSafeConciseToStringForBigNumbers() {
assertThat(ObjectUtils.nullSafeConciseToString(BigInteger.valueOf(42L))).isEqualTo("42");
assertThat(ObjectUtils.nullSafeConciseToString(BigDecimal.valueOf(99.1234D))).isEqualTo("99.1234");
}
@Test
void nullSafeConciseToStringForDate() {
Date date = new Date();
@@ -887,7 +955,37 @@ class ObjectUtilsTests {
}
@Test
void nullSafeConciseToStringForUri() {
void nullSafeConciseToStringForUUID() {
UUID id = UUID.randomUUID();
assertThat(ObjectUtils.nullSafeConciseToString(id)).isEqualTo(id.toString());
}
@Test
void nullSafeConciseToStringForFile() {
String path = "/tmp/file.txt".replace('/', File.separatorChar);
assertThat(ObjectUtils.nullSafeConciseToString(new File(path))).isEqualTo(path);
path = ("/tmp/" + repeat("xyz", 32)).replace('/', File.separatorChar);
assertThat(ObjectUtils.nullSafeConciseToString(new File(path)))
.hasSize(truncatedLength)
.startsWith(path.subSequence(0, 100))
.endsWith(truncated);
}
@Test
void nullSafeConciseToStringForPath() {
String path = "/tmp/file.txt".replace('/', File.separatorChar);
assertThat(ObjectUtils.nullSafeConciseToString(Paths.get(path))).isEqualTo(path);
path = ("/tmp/" + repeat("xyz", 32)).replace('/', File.separatorChar);
assertThat(ObjectUtils.nullSafeConciseToString(Paths.get(path)))
.hasSize(truncatedLength)
.startsWith(path.subSequence(0, 100))
.endsWith(truncated);
}
@Test
void nullSafeConciseToStringForURI() {
String uri = "https://www.example.com/?foo=1&bar=2&baz=3";
assertThat(ObjectUtils.nullSafeConciseToString(URI.create(uri))).isEqualTo(uri);
@@ -899,7 +997,7 @@ class ObjectUtilsTests {
}
@Test
void nullSafeConciseToStringForUrl() throws Exception {
void nullSafeConciseToStringForURL() throws Exception {
String url = "https://www.example.com/?foo=1&bar=2&baz=3";
assertThat(ObjectUtils.nullSafeConciseToString(new URL(url))).isEqualTo(url);
@@ -910,19 +1008,100 @@ class ObjectUtilsTests {
.endsWith(truncated);
}
@Test
void nullSafeConciseToStringForInetAddress() {
InetAddress localhost = getLocalhost();
assertThat(ObjectUtils.nullSafeConciseToString(localhost)).isEqualTo(localhost.toString());
}
private InetAddress getLocalhost() {
try {
return InetAddress.getLocalHost();
}
catch (UnknownHostException ex) {
return InetAddress.getLoopbackAddress();
}
}
@Test
void nullSafeConciseToStringForCharset() {
Charset charset = StandardCharsets.UTF_8;
assertThat(ObjectUtils.nullSafeConciseToString(charset)).isEqualTo(charset.name());
}
@Test
void nullSafeConciseToStringForCurrency() {
Currency currency = Currency.getInstance(Locale.US);
assertThat(ObjectUtils.nullSafeConciseToString(currency)).isEqualTo(currency.toString());
}
@Test
void nullSafeConciseToStringForLocale() {
assertThat(ObjectUtils.nullSafeConciseToString(Locale.GERMANY)).isEqualTo("de_DE");
}
@Test
void nullSafeConciseToStringForArraysAndCollections() {
List<String> list = Arrays.asList("a", "b", "c");
assertThat(ObjectUtils.nullSafeConciseToString(new int[][] {{1, 2}, {3, 4}})).startsWith(prefix(int[][].class));
assertThat(ObjectUtils.nullSafeConciseToString(list.toArray(new Object[0]))).startsWith(prefix(Object[].class));
assertThat(ObjectUtils.nullSafeConciseToString(list.toArray(new String[0]))).startsWith(prefix(String[].class));
assertThat(ObjectUtils.nullSafeConciseToString(new ArrayList<>(list))).startsWith(prefix(ArrayList.class));
assertThat(ObjectUtils.nullSafeConciseToString(new HashSet<>(list))).startsWith(prefix(HashSet.class));
void nullSafeConciseToStringForRegExPattern() {
Pattern pattern = Pattern.compile("^(foo|bar)$");
assertThat(ObjectUtils.nullSafeConciseToString(pattern)).isEqualTo(pattern.toString());
}
@Test
void nullSafeConciseToStringForTimeZone() {
TimeZone timeZone = TimeZone.getDefault();
assertThat(ObjectUtils.nullSafeConciseToString(timeZone)).isEqualTo(timeZone.getID());
}
@Test
void nullSafeConciseToStringForZoneId() {
ZoneId zoneId = ZoneId.systemDefault();
assertThat(ObjectUtils.nullSafeConciseToString(zoneId)).isEqualTo(zoneId.getId());
}
@Test
void nullSafeConciseToStringForEmptyArrays() {
assertThat(ObjectUtils.nullSafeConciseToString(new char[] {})).isEqualTo("{}");
assertThat(ObjectUtils.nullSafeConciseToString(new int[][] {})).isEqualTo("{}");
assertThat(ObjectUtils.nullSafeConciseToString(new String[] {})).isEqualTo("{}");
assertThat(ObjectUtils.nullSafeConciseToString(new Integer[][] {})).isEqualTo("{}");
}
@Test
void nullSafeConciseToStringForNonEmptyArrays() {
assertThat(ObjectUtils.nullSafeConciseToString(new char[] {'a'})).isEqualTo("{...}");
assertThat(ObjectUtils.nullSafeConciseToString(new int[][] {{1}, {2}})).isEqualTo("{...}");
assertThat(ObjectUtils.nullSafeConciseToString(new String[] {"enigma"})).isEqualTo("{...}");
assertThat(ObjectUtils.nullSafeConciseToString(new Integer[][] {{1}, {2}})).isEqualTo("{...}");
}
@Test
void nullSafeConciseToStringForEmptyCollections() {
List<String> list = Collections.emptyList();
Set<Integer> set = Collections.emptySet();
assertThat(ObjectUtils.nullSafeConciseToString(list)).isEqualTo("[]");
assertThat(ObjectUtils.nullSafeConciseToString(set)).isEqualTo("[]");
}
@Test
void nullSafeConciseToStringForNonEmptyCollections() {
List<String> list = Arrays.asList("a", "b");
Set<String> set = new HashSet<>();
set.add("foo");
assertThat(ObjectUtils.nullSafeConciseToString(list)).isEqualTo("[...]");
assertThat(ObjectUtils.nullSafeConciseToString(set)).isEqualTo("[...]");
}
@Test
void nullSafeConciseToStringForEmptyMaps() {
Map<String, Object> map = Collections.emptyMap();
assertThat(ObjectUtils.nullSafeConciseToString(map)).isEqualTo("{}");
}
@Test
void nullSafeConciseToStringForNonEmptyMaps() {
HashMap<String, Object> map = new HashMap<>();
map.put("foo", 42L);
assertThat(ObjectUtils.nullSafeConciseToString(map)).isEqualTo("{...}");
}
@Test
@@ -959,17 +1138,6 @@ class ObjectUtilsTests {
private String prefix(Class<?> clazz) {
return clazz.getTypeName() + "@";
}
}
private static void assertEqualHashCodes(int expected, Object array) {
int actual = ObjectUtils.nullSafeHashCode(array);
assertThat(actual).isEqualTo(expected);
assertThat(array.hashCode() != actual).isTrue();
}
enum Tropes {FOO, BAR, baz}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParseException;
import org.springframework.expression.ParserContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* An expression parser that understands templates. It can be subclassed by expression
@@ -34,6 +35,7 @@ import org.springframework.lang.Nullable;
* @author Keith Donald
* @author Juergen Hoeller
* @author Andy Clement
* @author Sam Brannen
* @since 3.0
*/
public abstract class TemplateAwareExpressionParser implements ExpressionParser {
@@ -46,9 +48,11 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
@Override
public Expression parseExpression(String expressionString, @Nullable ParserContext context) throws ParseException {
if (context != null && context.isTemplate()) {
Assert.notNull(expressionString, "'expressionString' must not be null");
return parseTemplate(expressionString, context);
}
else {
Assert.hasText(expressionString, "'expressionString' must not be null or blank");
return doParseExpression(expressionString, context);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,12 @@ import org.springframework.lang.Nullable;
*/
public class SpelParserConfiguration {
/**
* Default maximum length permitted for a SpEL expression.
* @since 5.2.24
*/
private static final int DEFAULT_MAX_EXPRESSION_LENGTH = 10_000;
/** System property to configure the default compiler mode for SpEL expression parsers: {@value}. */
public static final String SPRING_EXPRESSION_COMPILER_MODE_PROPERTY_NAME = "spring.expression.compiler.mode";
@@ -54,6 +60,8 @@ public class SpelParserConfiguration {
private final int maximumAutoGrowSize;
private final int maximumExpressionLength;
/**
* Create a new {@code SpelParserConfiguration} instance with default settings.
@@ -102,11 +110,30 @@ public class SpelParserConfiguration {
public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader,
boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize) {
this(compilerMode, compilerClassLoader, autoGrowNullReferences, autoGrowCollections,
maximumAutoGrowSize, DEFAULT_MAX_EXPRESSION_LENGTH);
}
/**
* Create a new {@code SpelParserConfiguration} instance.
* @param compilerMode the compiler mode that parsers using this configuration object should use
* @param compilerClassLoader the ClassLoader to use as the basis for expression compilation
* @param autoGrowNullReferences if null references should automatically grow
* @param autoGrowCollections if collections should automatically grow
* @param maximumAutoGrowSize the maximum size that a collection can auto grow
* @param maximumExpressionLength the maximum length of a SpEL expression;
* must be a positive number
* @since 5.2.25
*/
public SpelParserConfiguration(@Nullable SpelCompilerMode compilerMode, @Nullable ClassLoader compilerClassLoader,
boolean autoGrowNullReferences, boolean autoGrowCollections, int maximumAutoGrowSize, int maximumExpressionLength) {
this.compilerMode = (compilerMode != null ? compilerMode : defaultCompilerMode);
this.compilerClassLoader = compilerClassLoader;
this.autoGrowNullReferences = autoGrowNullReferences;
this.autoGrowCollections = autoGrowCollections;
this.maximumAutoGrowSize = maximumAutoGrowSize;
this.maximumExpressionLength = maximumExpressionLength;
}
@@ -146,4 +173,12 @@ public class SpelParserConfiguration {
return this.maximumAutoGrowSize;
}
/**
* Return the maximum number of characters that a SpEL expression can contain.
* @since 5.2.25
*/
public int getMaximumExpressionLength() {
return this.maximumExpressionLength;
}
}
@@ -93,13 +93,6 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private static final Pattern VALID_QUALIFIED_ID_PATTERN = Pattern.compile("[\\p{L}\\p{N}_$]+");
/**
* Maximum length permitted for a SpEL expression.
* @since 5.2.24
*/
private static final int MAX_EXPRESSION_LENGTH = 10_000;
private final SpelParserConfiguration configuration;
// For rules that build nodes, they are stacked here for return
@@ -158,8 +151,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
private void checkExpressionLength(String string) {
if (string.length() > MAX_EXPRESSION_LENGTH) {
throw new SpelEvaluationException(SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED, MAX_EXPRESSION_LENGTH);
int maxLength = this.configuration.getMaximumExpressionLength();
if (string.length() > maxLength) {
throw new SpelEvaluationException(SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED, maxLength);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@ import org.springframework.util.Assert;
*
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
*/
public class SpelExpressionParser extends TemplateAwareExpressionParser {
@@ -53,6 +54,7 @@ public class SpelExpressionParser extends TemplateAwareExpressionParser {
public SpelExpression parseRaw(String expressionString) throws ParseException {
Assert.hasText(expressionString, "'expressionString' must not be null or blank");
return doParseExpression(expressionString, null);
}
@@ -164,6 +164,24 @@ public abstract class AbstractExpressionTests {
*/
protected void evaluateAndCheckError(String expression, Class<?> expectedReturnType, SpelMessage expectedMessage,
Object... otherProperties) {
evaluateAndCheckError(this.parser, expression, expectedReturnType, expectedMessage, otherProperties);
}
/**
* Evaluate the specified expression and ensure the expected message comes out.
* The message may have inserts and they will be checked if otherProperties is specified.
* The first entry in otherProperties should always be the position.
* @param parser the expression parser to use
* @param expression the expression to evaluate
* @param expectedReturnType ask the expression return value to be of this type if possible
* ({@code null} indicates don't ask for conversion)
* @param expectedMessage the expected message
* @param otherProperties the expected inserts within the message
*/
protected void evaluateAndCheckError(ExpressionParser parser, String expression, Class<?> expectedReturnType, SpelMessage expectedMessage,
Object... otherProperties) {
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> {
Expression expr = parser.parseExpression(expression);
assertThat(expr).as("expression").isNotNull();
@@ -77,6 +77,26 @@ class EvaluationTests extends AbstractExpressionTests {
evaluateAndCheckError(expression, String.class, SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED);
}
@Test
void maxExpressionLengthIsConfigurable() {
int maximumExpressionLength = 20_000;
String expression = String.format("'%s'", repeat("Y", 19_998));
assertThat(expression).hasSize(maximumExpressionLength);
SpelParserConfiguration configuration =
new SpelParserConfiguration(null, null, false, false, 0, maximumExpressionLength);
ExpressionParser parser = new SpelExpressionParser(configuration);
Expression expr = parser.parseExpression(expression);
String result = expr.getValue(String.class);
assertThat(result).hasSize(19_998);
expression = String.format("'%s'", repeat("Y", 25_000));
assertThat(expression).hasSize(25_002);
evaluateAndCheckError(parser, expression, String.class, SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED);
}
@Test
void createListsOnAttemptToIndexNull01() throws EvaluationException, ParseException {
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,14 +30,16 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
*/
public class TemplateExpressionParsingTests extends AbstractExpressionTests {
class TemplateExpressionParsingTests extends AbstractExpressionTests {
public static final ParserContext DEFAULT_TEMPLATE_PARSER_CONTEXT = new ParserContext() {
static final ParserContext DEFAULT_TEMPLATE_PARSER_CONTEXT = new ParserContext() {
@Override
public String getExpressionPrefix() {
return "${";
@@ -52,7 +54,7 @@ public class TemplateExpressionParsingTests extends AbstractExpressionTests {
}
};
public static final ParserContext HASH_DELIMITED_PARSER_CONTEXT = new ParserContext() {
static final ParserContext HASH_DELIMITED_PARSER_CONTEXT = new ParserContext() {
@Override
public String getExpressionPrefix() {
return "#{";
@@ -68,25 +70,32 @@ public class TemplateExpressionParsingTests extends AbstractExpressionTests {
};
private final SpelExpressionParser parser = new SpelExpressionParser();
@Test
public void testParsingSimpleTemplateExpression01() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
void nullTemplateExpressionIsRejected() {
assertThatIllegalArgumentException()
.isThrownBy(() -> parser.parseExpression(null, DEFAULT_TEMPLATE_PARSER_CONTEXT))
.withMessage("'expressionString' must not be null");
}
@Test
void parsingSimpleTemplateExpression01() {
Expression expr = parser.parseExpression("hello ${'world'}", DEFAULT_TEMPLATE_PARSER_CONTEXT);
Object o = expr.getValue();
assertThat(o.toString()).isEqualTo("hello world");
}
@Test
public void testParsingSimpleTemplateExpression02() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
void parsingSimpleTemplateExpression02() {
Expression expr = parser.parseExpression("hello ${'to'} you", DEFAULT_TEMPLATE_PARSER_CONTEXT);
Object o = expr.getValue();
assertThat(o.toString()).isEqualTo("hello to you");
}
@Test
public void testParsingSimpleTemplateExpression03() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
void parsingSimpleTemplateExpression03() {
Expression expr = parser.parseExpression("The quick ${'brown'} fox jumped over the ${'lazy'} dog",
DEFAULT_TEMPLATE_PARSER_CONTEXT);
Object o = expr.getValue();
@@ -94,8 +103,7 @@ public class TemplateExpressionParsingTests extends AbstractExpressionTests {
}
@Test
public void testParsingSimpleTemplateExpression04() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
void parsingSimpleTemplateExpression04() {
Expression expr = parser.parseExpression("${'hello'} world", DEFAULT_TEMPLATE_PARSER_CONTEXT);
Object o = expr.getValue();
assertThat(o.toString()).isEqualTo("hello world");
@@ -114,8 +122,7 @@ public class TemplateExpressionParsingTests extends AbstractExpressionTests {
}
@Test
public void testCompositeStringExpression() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
void compositeStringExpression() {
Expression ex = parser.parseExpression("hello ${'world'}", DEFAULT_TEMPLATE_PARSER_CONTEXT);
assertThat(ex.getValue()).isInstanceOf(String.class).isEqualTo("hello world");
assertThat(ex.getValue(String.class)).isInstanceOf(String.class).isEqualTo("hello world");
@@ -154,8 +161,7 @@ public class TemplateExpressionParsingTests extends AbstractExpressionTests {
static class Rooty {}
@Test
public void testNestedExpressions() throws Exception {
SpelExpressionParser parser = new SpelExpressionParser();
void nestedExpressions() {
// treat the nested ${..} as a part of the expression
Expression ex = parser.parseExpression("hello ${listOfNumbersUpToTen.$[#this<5]} world",DEFAULT_TEMPLATE_PARSER_CONTEXT);
String s = ex.getValue(TestScenarioCreator.getTestEvaluationContext(),String.class);
@@ -185,8 +191,7 @@ public class TemplateExpressionParsingTests extends AbstractExpressionTests {
}
@Test
public void testClashingWithSuffixes() throws Exception {
void clashingWithSuffixes() {
// Just wanting to use the prefix or suffix within the template:
Expression ex = parser.parseExpression("hello ${3+4} world",DEFAULT_TEMPLATE_PARSER_CONTEXT);
String s = ex.getValue(TestScenarioCreator.getTestEvaluationContext(),String.class);
@@ -202,13 +207,13 @@ public class TemplateExpressionParsingTests extends AbstractExpressionTests {
}
@Test
public void testParsingNormalExpressionThroughTemplateParser() throws Exception {
void parsingNormalExpressionThroughTemplateParser() {
Expression expr = parser.parseExpression("1+2+3");
assertThat(expr.getValue()).isEqualTo(6);
}
@Test
public void testErrorCases() throws Exception {
void errorCases() {
assertThatExceptionOfType(ParseException.class).isThrownBy(() ->
parser.parseExpression("hello ${'world'", DEFAULT_TEMPLATE_PARSER_CONTEXT))
.satisfies(pex -> {
@@ -224,7 +229,7 @@ public class TemplateExpressionParsingTests extends AbstractExpressionTests {
}
@Test
public void testTemplateParserContext() {
void testTemplateParserContext() {
TemplateParserContext tpc = new TemplateParserContext("abc","def");
assertThat(tpc.getExpressionPrefix()).isEqualTo("abc");
assertThat(tpc.getExpressionSuffix()).isEqualTo("def");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@ package org.springframework.expression.spel.standard;
import java.util.function.Consumer;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.assertj.core.api.ThrowableAssertAlternative;
import org.junit.jupiter.api.Test;
import org.springframework.expression.EvaluationContext;
@@ -31,16 +33,55 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.springframework.expression.spel.SpelMessage.MISSING_CONSTRUCTOR_ARGS;
import static org.springframework.expression.spel.SpelMessage.NON_TERMINATING_DOUBLE_QUOTED_STRING;
import static org.springframework.expression.spel.SpelMessage.NON_TERMINATING_QUOTED_STRING;
import static org.springframework.expression.spel.SpelMessage.NOT_AN_INTEGER;
import static org.springframework.expression.spel.SpelMessage.NOT_A_LONG;
import static org.springframework.expression.spel.SpelMessage.REAL_CANNOT_BE_LONG;
import static org.springframework.expression.spel.SpelMessage.RUN_OUT_OF_ARGUMENTS;
import static org.springframework.expression.spel.SpelMessage.UNEXPECTED_DATA_AFTER_DOT;
import static org.springframework.expression.spel.SpelMessage.UNEXPECTED_ESCAPE_CHAR;
/**
* @author Andy Clement
* @author Juergen Hoeller
* @author Sam Brannen
*/
class SpelParserTests {
private final SpelExpressionParser parser = new SpelExpressionParser();
@Test
void nullExpressionIsRejected() {
assertNullOrEmptyExpressionIsRejected(() -> parser.parseExpression(null));
assertNullOrEmptyExpressionIsRejected(() -> parser.parseRaw(null));
}
@Test
void emptyExpressionIsRejected() {
assertNullOrEmptyExpressionIsRejected(() -> parser.parseExpression(""));
assertNullOrEmptyExpressionIsRejected(() -> parser.parseRaw(""));
}
@Test
void blankExpressionIsRejected() {
assertNullOrEmptyExpressionIsRejected(() -> parser.parseExpression(" "));
assertNullOrEmptyExpressionIsRejected(() -> parser.parseExpression("\t\n"));
assertNullOrEmptyExpressionIsRejected(() -> parser.parseRaw(" "));
assertNullOrEmptyExpressionIsRejected(() -> parser.parseRaw("\t\n"));
}
private static void assertNullOrEmptyExpressionIsRejected(ThrowingCallable throwingCallable) {
assertThatIllegalArgumentException()
.isThrownBy(throwingCallable)
.withMessage("'expressionString' must not be null or blank");
}
@Test
void theMostBasic() {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw("2");
assertThat(expr).isNotNull();
assertThat(expr.getAST()).isNotNull();
@@ -51,7 +92,6 @@ class SpelParserTests {
@Test
void valueType() {
SpelExpressionParser parser = new SpelExpressionParser();
EvaluationContext ctx = new StandardEvaluationContext();
Class<?> c = parser.parseRaw("2").getValueType();
assertThat(c).isEqualTo(Integer.class);
@@ -67,7 +107,6 @@ class SpelParserTests {
@Test
void whitespace() {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw("2 + 3");
assertThat(expr.getValue()).isEqualTo(5);
expr = parser.parseRaw("2 + 3");
@@ -80,7 +119,6 @@ class SpelParserTests {
@Test
void arithmeticPlus1() {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw("2+2");
assertThat(expr).isNotNull();
assertThat(expr.getAST()).isNotNull();
@@ -89,14 +127,12 @@ class SpelParserTests {
@Test
void arithmeticPlus2() {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw("37+41");
assertThat(expr.getValue()).isEqualTo(78);
}
@Test
void arithmeticMultiply1() {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw("2*3");
assertThat(expr).isNotNull();
assertThat(expr.getAST()).isNotNull();
@@ -105,162 +141,120 @@ class SpelParserTests {
@Test
void arithmeticPrecedence1() {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw("2*3+5");
assertThat(expr.getValue()).isEqualTo(11);
}
@Test
void generalExpressions() {
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> {
SpelExpressionParser parser = new SpelExpressionParser();
parser.parseRaw("new String");
})
.satisfies(parseExceptionRequirements(SpelMessage.MISSING_CONSTRUCTOR_ARGS, 10));
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> {
SpelExpressionParser parser = new SpelExpressionParser();
parser.parseRaw("new String(3,");
})
.satisfies(parseExceptionRequirements(SpelMessage.RUN_OUT_OF_ARGUMENTS, 10));
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> {
SpelExpressionParser parser = new SpelExpressionParser();
parser.parseRaw("new String(3");
})
.satisfies(parseExceptionRequirements(SpelMessage.RUN_OUT_OF_ARGUMENTS, 10));
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> {
SpelExpressionParser parser = new SpelExpressionParser();
parser.parseRaw("new String(");
})
.satisfies(parseExceptionRequirements(SpelMessage.RUN_OUT_OF_ARGUMENTS, 10));
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> {
SpelExpressionParser parser = new SpelExpressionParser();
parser.parseRaw("\"abc");
})
.satisfies(parseExceptionRequirements(SpelMessage.NON_TERMINATING_DOUBLE_QUOTED_STRING, 0));
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> {
SpelExpressionParser parser = new SpelExpressionParser();
parser.parseRaw("'abc");
})
.satisfies(parseExceptionRequirements(SpelMessage.NON_TERMINATING_QUOTED_STRING, 0));
}
private <E extends SpelParseException> Consumer<E> parseExceptionRequirements(
SpelMessage expectedMessage, int expectedPosition) {
return ex -> {
assertThat(ex.getMessageCode()).isEqualTo(expectedMessage);
assertThat(ex.getPosition()).isEqualTo(expectedPosition);
assertThat(ex.getMessage()).contains(ex.getExpressionString());
};
void parseExceptions() {
assertParseException(() -> parser.parseRaw("new String"), MISSING_CONSTRUCTOR_ARGS, 10);
assertParseException(() -> parser.parseRaw("new String(3,"), RUN_OUT_OF_ARGUMENTS, 10);
assertParseException(() -> parser.parseRaw("new String(3"), RUN_OUT_OF_ARGUMENTS, 10);
assertParseException(() -> parser.parseRaw("new String("), RUN_OUT_OF_ARGUMENTS, 10);
assertParseException(() -> parser.parseRaw("\"abc"), NON_TERMINATING_DOUBLE_QUOTED_STRING, 0);
assertParseException(() -> parser.parseRaw("'abc"), NON_TERMINATING_QUOTED_STRING, 0);
}
@Test
void arithmeticPrecedence2() {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw("2+3*5");
assertThat(expr.getValue()).isEqualTo(17);
}
@Test
void arithmeticPrecedence3() {
SpelExpression expr = new SpelExpressionParser().parseRaw("3+10/2");
SpelExpression expr = parser.parseRaw("3+10/2");
assertThat(expr.getValue()).isEqualTo(8);
}
@Test
void arithmeticPrecedence4() {
SpelExpression expr = new SpelExpressionParser().parseRaw("10/2+3");
SpelExpression expr = parser.parseRaw("10/2+3");
assertThat(expr.getValue()).isEqualTo(8);
}
@Test
void arithmeticPrecedence5() {
SpelExpression expr = new SpelExpressionParser().parseRaw("(4+10)/2");
SpelExpression expr = parser.parseRaw("(4+10)/2");
assertThat(expr.getValue()).isEqualTo(7);
}
@Test
void arithmeticPrecedence6() {
SpelExpression expr = new SpelExpressionParser().parseRaw("(3+2)*2");
SpelExpression expr = parser.parseRaw("(3+2)*2");
assertThat(expr.getValue()).isEqualTo(10);
}
@Test
void booleanOperators() {
SpelExpression expr = new SpelExpressionParser().parseRaw("true");
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.TRUE);
expr = new SpelExpressionParser().parseRaw("false");
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.FALSE);
expr = new SpelExpressionParser().parseRaw("false and false");
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.FALSE);
expr = new SpelExpressionParser().parseRaw("true and (true or false)");
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.TRUE);
expr = new SpelExpressionParser().parseRaw("true and true or false");
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.TRUE);
expr = new SpelExpressionParser().parseRaw("!true");
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.FALSE);
expr = new SpelExpressionParser().parseRaw("!(false or true)");
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.FALSE);
SpelExpression expr = parser.parseRaw("true");
assertThat(expr.getValue(Boolean.class)).isTrue();
expr = parser.parseRaw("false");
assertThat(expr.getValue(Boolean.class)).isFalse();
expr = parser.parseRaw("false and false");
assertThat(expr.getValue(Boolean.class)).isFalse();
expr = parser.parseRaw("true and (true or false)");
assertThat(expr.getValue(Boolean.class)).isTrue();
expr = parser.parseRaw("true and true or false");
assertThat(expr.getValue(Boolean.class)).isTrue();
expr = parser.parseRaw("!true");
assertThat(expr.getValue(Boolean.class)).isFalse();
expr = parser.parseRaw("!(false or true)");
assertThat(expr.getValue(Boolean.class)).isFalse();
}
@Test
void booleanOperators_symbolic_spr9614() {
SpelExpression expr = new SpelExpressionParser().parseRaw("true");
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.TRUE);
expr = new SpelExpressionParser().parseRaw("false");
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.FALSE);
expr = new SpelExpressionParser().parseRaw("false && false");
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.FALSE);
expr = new SpelExpressionParser().parseRaw("true && (true || false)");
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.TRUE);
expr = new SpelExpressionParser().parseRaw("true && true || false");
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.TRUE);
expr = new SpelExpressionParser().parseRaw("!true");
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.FALSE);
expr = new SpelExpressionParser().parseRaw("!(false || true)");
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.FALSE);
SpelExpression expr = parser.parseRaw("true");
assertThat(expr.getValue(Boolean.class)).isTrue();
expr = parser.parseRaw("false");
assertThat(expr.getValue(Boolean.class)).isFalse();
expr = parser.parseRaw("false && false");
assertThat(expr.getValue(Boolean.class)).isFalse();
expr = parser.parseRaw("true && (true || false)");
assertThat(expr.getValue(Boolean.class)).isTrue();
expr = parser.parseRaw("true && true || false");
assertThat(expr.getValue(Boolean.class)).isTrue();
expr = parser.parseRaw("!true");
assertThat(expr.getValue(Boolean.class)).isFalse();
expr = parser.parseRaw("!(false || true)");
assertThat(expr.getValue(Boolean.class)).isFalse();
}
@Test
void stringLiterals() {
SpelExpression expr = new SpelExpressionParser().parseRaw("'howdy'");
SpelExpression expr = parser.parseRaw("'howdy'");
assertThat(expr.getValue()).isEqualTo("howdy");
expr = new SpelExpressionParser().parseRaw("'hello '' world'");
expr = parser.parseRaw("'hello '' world'");
assertThat(expr.getValue()).isEqualTo("hello ' world");
}
@Test
void stringLiterals2() {
SpelExpression expr = new SpelExpressionParser().parseRaw("'howdy'.substring(0,2)");
SpelExpression expr = parser.parseRaw("'howdy'.substring(0,2)");
assertThat(expr.getValue()).isEqualTo("ho");
}
@Test
void testStringLiterals_DoubleQuotes_spr9620() {
SpelExpression expr = new SpelExpressionParser().parseRaw("\"double quote: \"\".\"");
SpelExpression expr = parser.parseRaw("\"double quote: \"\".\"");
assertThat(expr.getValue()).isEqualTo("double quote: \".");
expr = new SpelExpressionParser().parseRaw("\"hello \"\" world\"");
expr = parser.parseRaw("\"hello \"\" world\"");
assertThat(expr.getValue()).isEqualTo("hello \" world");
}
@Test
void testStringLiterals_DoubleQuotes_spr9620_2() {
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() ->
new SpelExpressionParser().parseRaw("\"double quote: \\\"\\\".\""))
assertParseExceptionThrownBy(() -> parser.parseRaw("\"double quote: \\\"\\\".\""))
.satisfies(ex -> {
assertThat(ex.getPosition()).isEqualTo(17);
assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.UNEXPECTED_ESCAPE_CHAR);
assertThat(ex.getMessageCode()).isEqualTo(UNEXPECTED_ESCAPE_CHAR);
});
}
@Test
void positionalInformation() {
SpelExpression expr = new SpelExpressionParser().parseRaw("true and true or false");
SpelExpression expr = parser.parseRaw("true and true or false");
SpelNode rootAst = expr.getAST();
OpOr operatorOr = (OpOr) rootAst;
OpAnd operatorAnd = (OpAnd) operatorOr.getLeftOperand();
@@ -355,10 +349,10 @@ class SpelParserTests {
checkNumber("0xa", 10, Integer.class);
checkNumber("0xAL", 10L, Long.class);
checkNumberError("0x", SpelMessage.NOT_AN_INTEGER);
checkNumberError("0xL", SpelMessage.NOT_A_LONG);
checkNumberError(".324", SpelMessage.UNEXPECTED_DATA_AFTER_DOT);
checkNumberError("3.4L", SpelMessage.REAL_CANNOT_BE_LONG);
checkNumberError("0x", NOT_AN_INTEGER);
checkNumberError("0xL", NOT_A_LONG);
checkNumberError(".324", UNEXPECTED_DATA_AFTER_DOT);
checkNumberError("3.4L", REAL_CANNOT_BE_LONG);
checkNumber("3.5f", 3.5f, Float.class);
checkNumber("1.2e3", 1.2e3d, Double.class);
@@ -371,7 +365,6 @@ class SpelParserTests {
private void checkNumber(String expression, Object value, Class<?> type) {
try {
SpelExpressionParser parser = new SpelExpressionParser();
SpelExpression expr = parser.parseRaw(expression);
Object exprVal = expr.getValue();
assertThat(exprVal).isEqualTo(value);
@@ -383,9 +376,26 @@ class SpelParserTests {
}
private void checkNumberError(String expression, SpelMessage expectedMessage) {
SpelExpressionParser parser = new SpelExpressionParser();
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> parser.parseRaw(expression))
assertParseExceptionThrownBy(() -> parser.parseRaw(expression))
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(expectedMessage));
}
private static ThrowableAssertAlternative<SpelParseException> assertParseExceptionThrownBy(ThrowingCallable throwingCallable) {
return assertThatExceptionOfType(SpelParseException.class).isThrownBy(throwingCallable);
}
private static void assertParseException(ThrowingCallable throwingCallable, SpelMessage expectedMessage, int expectedPosition) {
assertParseExceptionThrownBy(throwingCallable)
.satisfies(parseExceptionRequirements(expectedMessage, expectedPosition));
}
private static <E extends SpelParseException> Consumer<E> parseExceptionRequirements(
SpelMessage expectedMessage, int expectedPosition) {
return ex -> {
assertThat(ex.getMessageCode()).isEqualTo(expectedMessage);
assertThat(ex.getPosition()).isEqualTo(expectedPosition);
assertThat(ex.getMessage()).contains(ex.getExpressionString());
};
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -102,4 +102,47 @@ public abstract class LogFactory {
return getLog(name);
}
// Just in case some code happens to call uncommon Commons Logging methods...
@Deprecated
public Object getAttribute(String name) {
return null;
}
@Deprecated
public String[] getAttributeNames() {
return new String[0];
}
@Deprecated
public void removeAttribute(String name) {
// do nothing
}
@Deprecated
public void setAttribute(String name, Object value) {
// do nothing
}
@Deprecated
public void release() {
// do nothing
}
@Deprecated
public static void release(ClassLoader classLoader) {
// do nothing
}
@Deprecated
public static void releaseAll() {
// do nothing
}
@Deprecated
public static String objectId(Object o) {
return (o == null ? "null" : o.getClass().getName() + "@" + System.identityHashCode(o));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,8 +47,9 @@ public class LogFactoryService extends LogFactory {
}
// Just in case some code happens to call uncommon Commons Logging methods...
// Just in case some code happens to rely on Commons Logging attributes...
@Override
public void setAttribute(String name, Object value) {
if (value != null) {
this.attributes.put(name, value);
@@ -58,19 +59,19 @@ public class LogFactoryService extends LogFactory {
}
}
@Override
public void removeAttribute(String name) {
this.attributes.remove(name);
}
@Override
public Object getAttribute(String name) {
return this.attributes.get(name);
}
@Override
public String[] getAttributeNames() {
return this.attributes.keySet().toArray(new String[0]);
}
public void release() {
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,11 +44,22 @@ public class SQLWarningException extends UncategorizedDataAccessException {
super(msg, ex);
}
/**
* Return the underlying SQLWarning.
* Return the underlying {@link SQLWarning}.
* @since 5.3.29
*/
public SQLWarning SQLWarning() {
public SQLWarning getSQLWarning() {
return (SQLWarning) getCause();
}
/**
* Return the underlying {@link SQLWarning}.
* @deprecated as of 5.3.29, in favor of {@link #getSQLWarning()}
*/
@Deprecated
public SQLWarning SQLWarning() {
return getSQLWarning();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -187,12 +187,14 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
/**
* Set whether we want to ignore SQLWarnings.
* <p>Default is "true", swallowing and logging all warnings. Switch this flag
* to "false" to make the JdbcTemplate throw an SQLWarningException instead.
* Set whether we want to ignore JDBC statement warnings ({@link SQLWarning}).
* <p>Default is {@code true}, swallowing and logging all warnings. Switch this flag to
* {@code false} to make this JdbcTemplate throw a {@link SQLWarningException} instead
* (or chain the {@link SQLWarning} into the primary {@link SQLException}, if any).
* @see Statement#getWarnings()
* @see java.sql.SQLWarning
* @see org.springframework.jdbc.SQLWarningException
* @see #handleWarnings
* @see #handleWarnings(Statement)
*/
public void setIgnoreWarnings(boolean ignoreWarnings) {
this.ignoreWarnings = ignoreWarnings;
@@ -385,6 +387,9 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
if (stmt != null) {
handleWarnings(stmt, ex);
}
String sql = getSql(action);
JdbcUtils.closeStatement(stmt);
stmt = null;
@@ -658,6 +663,9 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
if (ps != null) {
handleWarnings(ps, ex);
}
String sql = getSql(psc);
psc = null;
JdbcUtils.closeStatement(ps);
@@ -1197,6 +1205,9 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
if (csc instanceof ParameterDisposer) {
((ParameterDisposer) csc).cleanupParameters();
}
if (cs != null) {
handleWarnings(cs, ex);
}
String sql = getSql(csc);
csc = null;
JdbcUtils.closeStatement(cs);
@@ -1494,13 +1505,44 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
}
/**
* Throw an SQLWarningException if we're not ignoring warnings,
* otherwise log the warnings at debug level.
* Handle warnings before propagating a primary {@code SQLException}
* from executing the given statement.
* <p>Calls regular {@link #handleWarnings(Statement)} but catches
* {@link SQLWarningException} in order to chain the {@link SQLWarning}
* into the primary exception instead.
* @param stmt the current JDBC statement
* @throws SQLWarningException if not ignoring warnings
* @see org.springframework.jdbc.SQLWarningException
* @param ex the primary exception after failed statement execution
* @since 5.3.29
* @see #handleWarnings(Statement)
* @see SQLException#setNextException
*/
protected void handleWarnings(Statement stmt) throws SQLException {
protected void handleWarnings(Statement stmt, SQLException ex) {
try {
handleWarnings(stmt);
}
catch (SQLWarningException nonIgnoredWarning) {
ex.setNextException(nonIgnoredWarning.getSQLWarning());
}
catch (SQLException warningsEx) {
logger.debug("Failed to retrieve warnings", warningsEx);
}
catch (Throwable warningsEx) {
logger.debug("Failed to process warnings", warningsEx);
}
}
/**
* Handle the warnings for the given JDBC statement, if any.
* <p>Throws a {@link SQLWarningException} if we're not ignoring warnings,
* otherwise logs the warnings at debug level.
* @param stmt the current JDBC statement
* @throws SQLException in case of warnings retrieval failure
* @throws SQLWarningException for a concrete warning to raise
* (when not ignoring warnings)
* @see #setIgnoreWarnings
* @see #handleWarnings(SQLWarning)
*/
protected void handleWarnings(Statement stmt) throws SQLException, SQLWarningException {
if (isIgnoreWarnings()) {
if (logger.isDebugEnabled()) {
SQLWarning warningToLog = stmt.getWarnings();
@@ -1517,7 +1559,7 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
}
/**
* Throw an SQLWarningException if encountering an actual warning.
* Throw a {@link SQLWarningException} if encountering an actual warning.
* @param warning the warnings object from the current statement.
* May be {@code null}, in which case this method does nothing.
* @throws SQLWarningException in case of an actual warning to be raised
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,49 +35,52 @@ import org.springframework.transaction.support.TransactionSynchronizationUtils;
import org.springframework.util.Assert;
/**
* {@link org.springframework.transaction.PlatformTransactionManager}
* implementation for a single JDBC {@link javax.sql.DataSource}. This class is
* capable of working in any environment with any JDBC driver, as long as the setup
* uses a {@code javax.sql.DataSource} as its {@code Connection} factory mechanism.
* Binds a JDBC Connection from the specified DataSource to the current thread,
* potentially allowing for one thread-bound Connection per DataSource.
* {@link org.springframework.transaction.PlatformTransactionManager} implementation
* for a single JDBC {@link javax.sql.DataSource}. This class is capable of working
* in any environment with any JDBC driver, as long as the setup uses a
* {@code javax.sql.DataSource} as its {@code Connection} factory mechanism.
* Binds a JDBC {@code Connection} from the specified {@code DataSource} to the
* current thread, potentially allowing for one thread-bound {@code Connection}
* per {@code DataSource}.
*
* <p><b>Note: The DataSource that this transaction manager operates on needs
* to return independent Connections.</b> The Connections may come from a pool
* (the typical case), but the DataSource must not return thread-scoped /
* request-scoped Connections or the like. This transaction manager will
* associate Connections with thread-bound transactions itself, according
* to the specified propagation behavior. It assumes that a separate,
* independent Connection can be obtained even during an ongoing transaction.
* <p><b>Note: The {@code DataSource} that this transaction manager operates on
* needs to return independent {@code Connection}s.</b> The {@code Connection}s
* typically come from a connection pool but the {@code DataSource} must not return
* specifically scoped or constrained {@code Connection}s. This transaction manager
* will associate {@code Connection}s with thread-bound transactions, according
* to the specified propagation behavior. It assumes that a separate, independent
* {@code Connection} can be obtained even during an ongoing transaction.
*
* <p>Application code is required to retrieve the JDBC Connection via
* <p>Application code is required to retrieve the JDBC {@code Connection} via
* {@link DataSourceUtils#getConnection(DataSource)} instead of a standard
* Java EE-style {@link DataSource#getConnection()} call. Spring classes such as
* EE-style {@link DataSource#getConnection()} call. Spring classes such as
* {@link org.springframework.jdbc.core.JdbcTemplate} use this strategy implicitly.
* If not used in combination with this transaction manager, the
* {@link DataSourceUtils} lookup strategy behaves exactly like the native
* DataSource lookup; it can thus be used in a portable fashion.
* {@code DataSource} lookup; it can thus be used in a portable fashion.
*
* <p>Alternatively, you can allow application code to work with the standard
* Java EE-style lookup pattern {@link DataSource#getConnection()}, for example for
* legacy code that is not aware of Spring at all. In that case, define a
* {@link TransactionAwareDataSourceProxy} for your target DataSource, and pass
* that proxy DataSource to your DAOs, which will automatically participate in
* Spring-managed transactions when accessing it.
* EE-style lookup pattern {@link DataSource#getConnection()}, for example
* for legacy code that is not aware of Spring at all. In that case, define a
* {@link TransactionAwareDataSourceProxy} for your target {@code DataSource},
* and pass that proxy {@code DataSource} to your DAOs which will automatically
* participate in Spring-managed transactions when accessing it.
*
* <p>Supports custom isolation levels, and timeouts which get applied as
* appropriate JDBC statement timeouts. To support the latter, application code
* must either use {@link org.springframework.jdbc.core.JdbcTemplate}, call
* {@link DataSourceUtils#applyTransactionTimeout} for each created JDBC Statement,
* or go through a {@link TransactionAwareDataSourceProxy} which will create
* timeout-aware JDBC Connections and Statements automatically.
* {@link DataSourceUtils#applyTransactionTimeout} for each created JDBC
* {@code Statement}, or go through a {@link TransactionAwareDataSourceProxy}
* which will create timeout-aware JDBC {@code Connection}s and {@code Statement}s
* automatically.
*
* <p>Consider defining a {@link LazyConnectionDataSourceProxy} for your target
* DataSource, pointing both this transaction manager and your DAOs to it.
* {@code DataSource}, pointing both this transaction manager and your DAOs to it.
* This will lead to optimized handling of "empty" transactions, i.e. of transactions
* without any JDBC statements executed. A LazyConnectionDataSourceProxy will not fetch
* an actual JDBC Connection from the target DataSource until a Statement gets executed,
* lazily applying the specified transaction settings to the target Connection.
* without any JDBC statements executed. A {@code LazyConnectionDataSourceProxy} will
* not fetch an actual JDBC {@code Connection} from the target {@code DataSource}
* until a {@code Statement} gets executed, lazily applying the specified transaction
* settings to the target {@code Connection}.
*
* <p>This transaction manager supports nested transactions via the JDBC 3.0
* {@link java.sql.Savepoint} mechanism. The
@@ -88,9 +91,9 @@ import org.springframework.util.Assert;
* <p>This transaction manager can be used as a replacement for the
* {@link org.springframework.transaction.jta.JtaTransactionManager} in the single
* resource case, as it does not require a container that supports JTA, typically
* in combination with a locally defined JDBC DataSource (e.g. an Apache Commons
* DBCP connection pool). Switching between this local strategy and a JTA
* environment is just a matter of configuration!
* in combination with a locally defined JDBC {@code DataSource} (e.g. a Hikari
* connection pool). Switching between this local strategy and a JTA environment
* is just a matter of configuration!
*
* <p>As of 4.3.4, this transaction manager triggers flush callbacks on registered
* transaction synchronizations (if synchronization is generally active), assuming
@@ -112,6 +115,7 @@ import org.springframework.util.Assert;
* @see TransactionAwareDataSourceProxy
* @see LazyConnectionDataSourceProxy
* @see org.springframework.jdbc.core.JdbcTemplate
* @see org.springframework.jdbc.support.JdbcTransactionManager
*/
@SuppressWarnings("serial")
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
@@ -124,8 +128,8 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan
/**
* Create a new DataSourceTransactionManager instance.
* A DataSource has to be set to be able to use it.
* Create a new {@code DataSourceTransactionManager} instance.
* A {@code DataSource} has to be set to be able to use it.
* @see #setDataSource
*/
public DataSourceTransactionManager() {
@@ -133,7 +137,7 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan
}
/**
* Create a new DataSourceTransactionManager instance.
* Create a new {@code DataSourceTransactionManager} instance.
* @param dataSource the JDBC DataSource to manage transactions for
*/
public DataSourceTransactionManager(DataSource dataSource) {
@@ -144,22 +148,22 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan
/**
* Set the JDBC DataSource that this instance should manage transactions for.
* <p>This will typically be a locally defined DataSource, for example an
* Apache Commons DBCP connection pool. Alternatively, you can also drive
* transactions for a non-XA J2EE DataSource fetched from JNDI. For an XA
* DataSource, use JtaTransactionManager.
* <p>The DataSource specified here should be the target DataSource to manage
* transactions for, not a TransactionAwareDataSourceProxy. Only data access
* code may work with TransactionAwareDataSourceProxy, while the transaction
* manager needs to work on the underlying target DataSource. If there's
* nevertheless a TransactionAwareDataSourceProxy passed in, it will be
* unwrapped to extract its target DataSource.
* <p><b>The DataSource passed in here needs to return independent Connections.</b>
* The Connections may come from a pool (the typical case), but the DataSource
* must not return thread-scoped / request-scoped Connections or the like.
* @see TransactionAwareDataSourceProxy
* @see org.springframework.transaction.jta.JtaTransactionManager
* Set the JDBC {@code DataSource} that this instance should manage transactions for.
* <p>This will typically be a locally defined {@code DataSource}, for example a
* Hikari connection pool. Alternatively, you can also manage transactions for a
* non-XA {@code DataSource} fetched from JNDI. For an XA {@code DataSource},
* use {@link org.springframework.transaction.jta.JtaTransactionManager} instead.
* <p>The {@code DataSource} specified here should be the target {@code DataSource}
* to manage transactions for, not a {@link TransactionAwareDataSourceProxy}.
* Only data access code may work with {@code TransactionAwareDataSourceProxy} while
* the transaction manager needs to work on the underlying target {@code DataSource}.
* If there is nevertheless a {@code TransactionAwareDataSourceProxy} passed in,
* it will be unwrapped to extract its target {@code DataSource}.
* <p><b>The {@code DataSource} passed in here needs to return independent
* {@code Connection}s.</b> The {@code Connection}s typically come from a
* connection pool but the {@code DataSource} must not return specifically
* scoped or constrained {@code Connection}s, just possibly lazily fetched.
* @see LazyConnectionDataSourceProxy
*/
public void setDataSource(@Nullable DataSource dataSource) {
if (dataSource instanceof TransactionAwareDataSourceProxy) {
@@ -174,7 +178,7 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan
}
/**
* Return the JDBC DataSource that this instance manages transactions for.
* Return the JDBC {@code DataSource} that this instance manages transactions for.
*/
@Nullable
public DataSource getDataSource() {
@@ -182,7 +186,7 @@ public class DataSourceTransactionManager extends AbstractPlatformTransactionMan
}
/**
* Obtain the DataSource for actual use.
* Obtain the {@code DataSource} for actual use.
* @return the DataSource (never {@code null})
* @throws IllegalStateException in case of no DataSource set
* @since 5.0
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,9 +33,9 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
import org.springframework.util.Assert;
/**
* Helper class that provides static methods for obtaining JDBC Connections from
* a {@link javax.sql.DataSource}. Includes special support for Spring-managed
* transactional Connections, e.g. managed by {@link DataSourceTransactionManager}
* Helper class that provides static methods for obtaining JDBC {@code Connection}s
* from a {@link javax.sql.DataSource}. Includes special support for Spring-managed
* transactional {@code Connection}s, e.g. managed by {@link DataSourceTransactionManager}
* or {@link org.springframework.transaction.jta.JtaTransactionManager}.
*
* <p>Used internally by Spring's {@link org.springframework.jdbc.core.JdbcTemplate},
@@ -46,7 +46,8 @@ import org.springframework.util.Assert;
* @author Juergen Hoeller
* @see #getConnection
* @see #releaseConnection
* @see DataSourceTransactionManager
* @see org.springframework.jdbc.core.JdbcTemplate
* @see org.springframework.jdbc.support.JdbcTransactionManager
* @see org.springframework.transaction.jta.JtaTransactionManager
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
@@ -474,7 +475,7 @@ public abstract class DataSourceUtils {
private final DataSource dataSource;
private int order;
private final int order;
private boolean holderActive = true;
@@ -172,7 +172,7 @@
<value>2601,2627</value>
</property>
<property name="dataIntegrityViolationCodes">
<value>544,8114,8115</value>
<value>544,2628,8114,8115</value>
</property>
<property name="dataAccessResourceFailureCodes">
<value>4060</value>
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,8 +27,8 @@ import org.springframework.util.backoff.BackOff;
* A {@link JmsListenerContainerFactory} implementation to build a regular
* {@link DefaultMessageListenerContainer}.
*
* <p>This should be the default for most users and a good transition paths
* for those that are used to build such container definition manually.
* <p>This should be the default for most users and a good transition path
* for those who are used to building such a container definition manually.
*
* @author Stephane Nicoll
* @since 4.1
@@ -67,63 +67,63 @@ public class DefaultJmsListenerContainerFactory
/**
* @see DefaultMessageListenerContainer#setTaskExecutor
*/
public void setTaskExecutor(Executor taskExecutor) {
public void setTaskExecutor(@Nullable Executor taskExecutor) {
this.taskExecutor = taskExecutor;
}
/**
* @see DefaultMessageListenerContainer#setTransactionManager
*/
public void setTransactionManager(PlatformTransactionManager transactionManager) {
public void setTransactionManager(@Nullable PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* @see DefaultMessageListenerContainer#setCacheLevel
*/
public void setCacheLevel(Integer cacheLevel) {
public void setCacheLevel(@Nullable Integer cacheLevel) {
this.cacheLevel = cacheLevel;
}
/**
* @see DefaultMessageListenerContainer#setCacheLevelName
*/
public void setCacheLevelName(String cacheLevelName) {
public void setCacheLevelName(@Nullable String cacheLevelName) {
this.cacheLevelName = cacheLevelName;
}
/**
* @see DefaultMessageListenerContainer#setConcurrency
*/
public void setConcurrency(String concurrency) {
public void setConcurrency(@Nullable String concurrency) {
this.concurrency = concurrency;
}
/**
* @see DefaultMessageListenerContainer#setMaxMessagesPerTask
*/
public void setMaxMessagesPerTask(Integer maxMessagesPerTask) {
public void setMaxMessagesPerTask(@Nullable Integer maxMessagesPerTask) {
this.maxMessagesPerTask = maxMessagesPerTask;
}
/**
* @see DefaultMessageListenerContainer#setReceiveTimeout
*/
public void setReceiveTimeout(Long receiveTimeout) {
public void setReceiveTimeout(@Nullable Long receiveTimeout) {
this.receiveTimeout = receiveTimeout;
}
/**
* @see DefaultMessageListenerContainer#setRecoveryInterval
*/
public void setRecoveryInterval(Long recoveryInterval) {
public void setRecoveryInterval(@Nullable Long recoveryInterval) {
this.recoveryInterval = recoveryInterval;
}
/**
* @see DefaultMessageListenerContainer#setBackOff
*/
public void setBackOff(BackOff backOff) {
public void setBackOff(@Nullable BackOff backOff) {
this.backOff = backOff;
}
@@ -125,18 +125,19 @@ public abstract class AbstractJmsListeningContainer extends JmsDestinationAccess
}
/**
* Specify the phase in which this container should be started and
* stopped. The startup order proceeds from lowest to highest, and
* the shutdown order is the reverse of that. By default this value
* is Integer.MAX_VALUE meaning that this container starts as late
* as possible and stops as soon as possible.
* Specify the lifecycle phase in which this container should be started and stopped.
* <p>The startup order proceeds from lowest to highest, and the shutdown order
* is the reverse of that. The default is {@link #DEFAULT_PHASE} meaning that
* this container starts as late as possible and stops as soon as possible.
* @see SmartLifecycle#getPhase()
*/
public void setPhase(int phase) {
this.phase = phase;
}
/**
* Return the phase in which this container will be started and stopped.
* Return the lifecycle phase in which this container will be started and stopped.
* @see #setPhase
*/
@Override
public int getPhase() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -212,7 +212,7 @@ public class HandlerMethod {
/**
* If the bean method is a bridge method, this method returns the bridged
* (user-defined) method. Otherwise it returns the same method as {@link #getMethod()}.
* (user-defined) method. Otherwise, it returns the same method as {@link #getMethod()}.
*/
protected Method getBridgedMethod() {
return this.bridgedMethod;
@@ -298,8 +298,8 @@ public class HandlerMethod {
* Return a short representation of this handler method for log message purposes.
*/
public String getShortLogMessage() {
int args = this.method.getParameterCount();
return getBeanType().getSimpleName() + "#" + this.method.getName() + "[" + args + " args]";
return getBeanType().getSimpleName() + "#" + this.method.getName() +
"[" + this.method.getParameterCount() + " args]";
}
@@ -365,13 +365,11 @@ public class HandlerMethod {
}
protected String formatInvokeError(String text, Object[] args) {
String formattedArgs = IntStream.range(0, args.length)
.mapToObj(i -> (args[i] != null ?
"[" + i + "] [type=" + args[i].getClass().getName() + "] [value=" + args[i] + "]" :
"[" + i + "] [null]"))
.collect(Collectors.joining(",\n", " ", " "));
return text + "\n" +
"Endpoint [" + getBeanType().getName() + "]\n" +
"Method [" + getBridgedMethod().toGenericString() + "] " +
@@ -613,11 +613,12 @@ public class ResolvableMethod {
private static class MethodInvocationInterceptor implements MethodInterceptor, InvocationHandler {
@Nullable
private Method invokedMethod;
@Override
@Nullable
public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) {
public Object intercept(Object object, Method method, @Nullable Object[] args, @Nullable MethodProxy proxy) {
if (ReflectionUtils.isObjectMethod(method)) {
return ReflectionUtils.invokeMethod(method, object, args);
}
@@ -629,10 +630,11 @@ public class ResolvableMethod {
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
public Object invoke(Object proxy, Method method, @Nullable Object[] args) {
return intercept(proxy, method, args, null);
}
@Nullable
Method getInvokedMethod() {
return this.invokedMethod;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -100,13 +100,11 @@ import org.springframework.util.Assert;
* @author Juergen Hoeller
* @since 4.2
* @see #setSessionFactory
* @see #setDataSource
* @see SessionFactory#getCurrentSession()
* @see DataSourceUtils#getConnection
* @see DataSourceUtils#releaseConnection
* @see org.springframework.jdbc.core.JdbcTemplate
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
* @see org.springframework.transaction.jta.JtaTransactionManager
* @see org.springframework.jdbc.support.JdbcTransactionManager
* @see org.springframework.orm.jpa.JpaTransactionManager
* @see org.springframework.orm.jpa.vendor.HibernateJpaDialect
*/
@SuppressWarnings("serial")
public class HibernateTransactionManager extends AbstractPlatformTransactionManager
@@ -271,7 +269,11 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
* @see Connection#setHoldability
* @see ResultSet#HOLD_CURSORS_OVER_COMMIT
* @see #disconnectOnCompletion(Session)
* @deprecated as of 5.3.29 since Hibernate 5.x aggressively closes ResultSets on commit,
* making it impossible to rely on ResultSet holdability. Also, Spring does not provide
* an equivalent setting on {@link org.springframework.orm.jpa.JpaTransactionManager}.
*/
@Deprecated
public void setAllowResultAccessAfterCompletion(boolean allowResultAccessAfterCompletion) {
this.allowResultAccessAfterCompletion = allowResultAccessAfterCompletion;
}

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