mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
251 Commits
main
...
v5.2.23.RELEASE
| Author | SHA1 | Date | |
|---|---|---|---|
| e424670f37 | |||
| b9b31afcc9 | |||
| 4542b53103 | |||
| 52c93b1c4b | |||
| 9c6cb744d4 | |||
| 1488b67712 | |||
| e163078a56 | |||
| 9f238c997d | |||
| 50177b1ad3 | |||
| 159a99bbaf | |||
| 41e158c93d | |||
| 833e750175 | |||
| d70054d686 | |||
| 36e4951b5e | |||
| 87b5080586 | |||
| 5cbf85a6f8 | |||
| 69c7eb9418 | |||
| fb763dd0f4 | |||
| 1b75f393a9 | |||
| 996f701a19 | |||
| 90cfde985e | |||
| 94f52bc94f | |||
| d4478bafa4 | |||
| 136e6db822 | |||
| 8f1f68377d | |||
| ce2367a76f | |||
| acf782347f | |||
| 1a03ffef62 | |||
| e954d7fc20 | |||
| ec13a3f1ba | |||
| 842f5b354d | |||
| 67a4b63d42 | |||
| 354dad615b | |||
| 0668323176 | |||
| e03e62c76a | |||
| 96a7fc693b | |||
| 50f6db2704 | |||
| 24ebb5ecd5 | |||
| a439a83f34 | |||
| f0de3a9924 | |||
| 17c3976bca | |||
| 9c4a33e64c | |||
| dd3ae4e63d | |||
| dfc435d045 | |||
| 78a24c0dd1 | |||
| 2bec54e7b3 | |||
| 52e2953de9 | |||
| 7b149a68c6 | |||
| d1b3b8eaf6 | |||
| f94794e5c0 | |||
| 26af506441 | |||
| b14a940716 | |||
| 4ecbf6511d | |||
| 5b6a32d1b9 | |||
| 263244f637 | |||
| 3a166ea742 | |||
| 6d4dfed772 | |||
| 09ef597836 | |||
| 50920e1a96 | |||
| 8c4d1d4d29 | |||
| 82f8d0dfd7 | |||
| 38f616a8e8 | |||
| 441c4d5d5c | |||
| 66f4d20fd4 | |||
| 2fa44d62b9 | |||
| c86ac97267 | |||
| fd916baf79 | |||
| 790d56dd18 | |||
| 5a5f118efc | |||
| edf664c1cf | |||
| 137bca129f | |||
| b01a3aeb6b | |||
| 195d375964 | |||
| ebd351753f | |||
| 9524ecd5b0 | |||
| 9053613a65 | |||
| 0aa7ba80e1 | |||
| 9a05a8846b | |||
| fb97a126a5 | |||
| 1ad5b4fbd6 | |||
| 4d1cdf6379 | |||
| e1ba3e7dbb | |||
| 2b55b9ccd5 | |||
| 90bcb2e375 | |||
| 9ad3464c80 | |||
| 1954a22c23 | |||
| bd90c4d0f0 | |||
| e31d66cd2b | |||
| eb349be1fc | |||
| 4b3e8619ec | |||
| 50d597eb41 | |||
| 9c931f669c | |||
| b7e6169e12 | |||
| 9c204dc475 | |||
| 21c0e50b33 | |||
| cecd2ee2e6 | |||
| 97a89d6db9 | |||
| 10a90d3204 | |||
| ab2a861163 | |||
| f56d6ea1fb | |||
| 420aa9bef0 | |||
| 14adefa867 | |||
| fe3357d6b9 | |||
| 11c51d8633 | |||
| bdbd999619 | |||
| bc4af15e9e | |||
| 348dc826f9 | |||
| 43901b2c6a | |||
| 7b34bf2a6c | |||
| c5a138a167 | |||
| 1f098e1f22 | |||
| 67fccc3c7e | |||
| 70e6606f0f | |||
| 5b55cefd64 | |||
| 911aca17ea | |||
| 90183568ba | |||
| b5ce514b27 | |||
| 9ae6268a1a | |||
| e4d843e41e | |||
| 22256966fb | |||
| 7c74459cb0 | |||
| 85ad63bd35 | |||
| a2ff769bc1 | |||
| 9523f1ffd6 | |||
| cd0570ee39 | |||
| 05d767b1cf | |||
| 30655555bd | |||
| 0ee8790a9a | |||
| bcd8896689 | |||
| 53b8d73078 | |||
| 3ab270e0f1 | |||
| ddb727bee8 | |||
| a33adac6c8 | |||
| 08663d4792 | |||
| 30b5a7b5c3 | |||
| 620023cbc3 | |||
| 19474e2d03 | |||
| 43071109f7 | |||
| bb530dca5b | |||
| e4e2212817 | |||
| 34fcbfbcc5 | |||
| 20cbd684d4 | |||
| 5f9a6ce98e | |||
| 07e18df0b9 | |||
| 4a6fea353f | |||
| f76504512e | |||
| 215514f27b | |||
| ae2711866a | |||
| a6ab7164a3 | |||
| a7db92b44e | |||
| 63912f802a | |||
| 3fbf3a597a | |||
| 5291c5b233 | |||
| c978fb4466 | |||
| 0fc831e8bd | |||
| dd967215fe | |||
| 31c5fc6417 | |||
| e8f685ecc8 | |||
| 428dbc43da | |||
| dd3262bfe3 | |||
| fdafd38d2f | |||
| 74b248a6b3 | |||
| 60e418959a | |||
| 4530e36867 | |||
| 2ec7d5c785 | |||
| 9f7c8aea94 | |||
| 792656097a | |||
| 33add3350b | |||
| 8f2010d669 | |||
| 9d4bfb1769 | |||
| da5f410af4 | |||
| 940f57d023 | |||
| 29799be474 | |||
| 67979c93c3 | |||
| 4ae3ab14ec | |||
| 81f0d76f3d | |||
| 9f1e822f3e | |||
| b4baa86bfa | |||
| 81be4c22d0 | |||
| a17c2ccb4e | |||
| 64df93168d | |||
| 27c5480c82 | |||
| 594ec8f8a9 | |||
| c09b2513e0 | |||
| 6c22f7ef5e | |||
| 236623f630 | |||
| e18fba1626 | |||
| 070c596a66 | |||
| 7a9bf1578e | |||
| 8a150ee3a4 | |||
| 6051f4ecbd | |||
| 698e74f7cd | |||
| 51079a40ae | |||
| 94ac2e4de7 | |||
| 271a9097ac | |||
| 3c030edbf2 | |||
| 059cff52b7 | |||
| 619a3edae5 | |||
| eb3811e590 | |||
| f011e58f90 | |||
| 7325a863bb | |||
| fe26b7d3fd | |||
| cfdceae70f | |||
| 17647801aa | |||
| 33476a2eae | |||
| a8091b916b | |||
| 396fdf125f | |||
| 085825fbc0 | |||
| b0c6e5e322 | |||
| a7efa9659a | |||
| f7605eaf58 | |||
| ce67b89861 | |||
| 736af46fc0 | |||
| a495bd6679 | |||
| 7c33c70742 | |||
| 81c1b60f19 | |||
| 3703be5aaf | |||
| b929edb221 | |||
| 50803ce142 | |||
| 346445ee7e | |||
| e238c8a87c | |||
| 9e99fd5df2 | |||
| 990c74b1e2 | |||
| 37bda566eb | |||
| 322babc04a | |||
| 4e720e8104 | |||
| fdab75a1d8 | |||
| 240cfb1224 | |||
| db2786264b | |||
| 763fa98bdf | |||
| 25cbc263f0 | |||
| 9949a91048 | |||
| d8b7a593c5 | |||
| d3d8f1a487 | |||
| a637f6a27c | |||
| e9416b369e | |||
| cde95e1446 | |||
| da4e37dc1d | |||
| 010d0947c7 | |||
| 5b06c23a1b | |||
| 7881329cf7 | |||
| 58aa0659cc | |||
| dbbedc6c86 | |||
| 09c1e986b9 | |||
| 10bff054a9 | |||
| e713e0d6d5 | |||
| b4f8fc8177 | |||
| 8c3cdc6118 | |||
| 01827fd8d2 | |||
| af1d721aa3 | |||
| 0acb1e5513 |
@@ -1,4 +1,4 @@
|
||||
# <img src="src/docs/asciidoc/images/spring-framework.png" width="80" height="80"> Spring Framework [](https://build.spring.io/browse/SPR)
|
||||
# <img src="src/docs/asciidoc/images/spring-framework.png" width="80" height="80"> Spring Framework [](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.2.x?groups=Build")
|
||||
|
||||
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
|
||||
|
||||
|
||||
+1
-1
@@ -8,4 +8,4 @@ wiki page.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please see https://pivotal.io/security.
|
||||
Please see https://spring.io/security-policy.
|
||||
|
||||
+23
-24
@@ -1,16 +1,15 @@
|
||||
plugins {
|
||||
id 'io.spring.dependency-management' version '1.0.9.RELEASE' apply false
|
||||
id 'io.spring.gradle-enterprise-conventions' version '0.0.2'
|
||||
id 'io.spring.ge.conventions' version '0.0.7'
|
||||
id 'io.spring.nohttp' version '0.0.5.RELEASE'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.72' apply false
|
||||
id "io.freefair.aspectj" version '4.1.6' apply false
|
||||
id 'org.jetbrains.dokka' version '0.10.1' apply false
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.72' apply false
|
||||
id 'org.asciidoctor.jvm.convert' version '2.4.0'
|
||||
id 'org.asciidoctor.jvm.pdf' version '2.4.0'
|
||||
id 'de.undercouch.download' version '4.1.1'
|
||||
id "io.freefair.aspectj" version '4.1.6' apply false
|
||||
id "com.github.ben-manes.versions" version '0.28.0'
|
||||
id 'com.gradle.build-scan' version '3.2'
|
||||
id "com.jfrog.artifactory" version '4.12.0' apply false
|
||||
id "com.github.ben-manes.versions" version '0.24.0'
|
||||
id 'de.undercouch.download' version '4.1.1'
|
||||
}
|
||||
|
||||
apply from: "$rootDir/gradle/build-scan-user-data.gradle"
|
||||
@@ -29,29 +28,29 @@ configure(allprojects) { project ->
|
||||
imports {
|
||||
mavenBom "com.fasterxml.jackson:jackson-bom:2.10.5"
|
||||
mavenBom "io.netty:netty-bom:4.1.51.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:Dysprosium-SR13"
|
||||
mavenBom "io.rsocket:rsocket-bom:1.0.3"
|
||||
mavenBom "io.projectreactor:reactor-bom:Dysprosium-SR25"
|
||||
mavenBom "io.rsocket:rsocket-bom:1.0.4"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.31.v20200723"
|
||||
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.3.72"
|
||||
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.3.5"
|
||||
mavenBom "org.junit:junit-bom:5.6.3"
|
||||
}
|
||||
dependencies {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.13.3') {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.17.2') {
|
||||
entry 'log4j-api'
|
||||
entry 'log4j-core'
|
||||
entry 'log4j-jul'
|
||||
entry 'log4j-slf4j-impl'
|
||||
}
|
||||
dependency "org.slf4j:slf4j-api:1.7.30"
|
||||
dependency "org.slf4j:slf4j-api:1.7.32"
|
||||
dependency "com.google.code.findbugs:jsr305:3.0.2"
|
||||
|
||||
dependencySet(group: 'org.aspectj', version: '1.9.6') {
|
||||
dependencySet(group: 'org.aspectj', version: '1.9.7') {
|
||||
entry 'aspectjrt'
|
||||
entry 'aspectjtools'
|
||||
entry 'aspectjweaver'
|
||||
}
|
||||
dependencySet(group: 'org.codehaus.groovy', version: '2.5.13') {
|
||||
dependencySet(group: 'org.codehaus.groovy', version: '2.5.15') {
|
||||
entry 'groovy'
|
||||
entry 'groovy-jsr223'
|
||||
entry 'groovy-templates'
|
||||
@@ -61,8 +60,8 @@ configure(allprojects) { project ->
|
||||
|
||||
dependency "io.reactivex:rxjava:1.3.8"
|
||||
dependency "io.reactivex:rxjava-reactive-streams:1.2.1"
|
||||
dependency "io.reactivex.rxjava2:rxjava:2.2.19"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.4.RELEASE"
|
||||
dependency "io.reactivex.rxjava2:rxjava:2.2.21"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.6.RELEASE"
|
||||
|
||||
dependency "com.caucho:hessian:4.0.63"
|
||||
dependency "com.fasterxml:aalto-xml:1.2.2"
|
||||
@@ -76,7 +75,7 @@ configure(allprojects) { project ->
|
||||
exclude group: "xpp3", name: "xpp3_min"
|
||||
exclude group: "xmlpull", name: "xmlpull"
|
||||
}
|
||||
dependency "org.apache.johnzon:johnzon-jsonb:1.2.8"
|
||||
dependency "org.apache.johnzon:johnzon-jsonb:1.2.10"
|
||||
dependency("org.codehaus.jettison:jettison:1.3.8") {
|
||||
exclude group: "stax", name: "stax-api"
|
||||
}
|
||||
@@ -88,8 +87,8 @@ configure(allprojects) { project ->
|
||||
dependency "org.yaml:snakeyaml:1.27"
|
||||
|
||||
dependency "com.h2database:h2:1.4.200"
|
||||
dependency "com.github.ben-manes.caffeine:caffeine:2.8.6"
|
||||
dependency "com.github.librepdf:openpdf:1.3.22"
|
||||
dependency "com.github.ben-manes.caffeine:caffeine:2.8.8"
|
||||
dependency "com.github.librepdf:openpdf:1.3.25"
|
||||
dependency "com.rometools:rome:1.12.2"
|
||||
dependency "commons-io:commons-io:2.5"
|
||||
dependency "io.vavr:vavr:0.10.3"
|
||||
@@ -116,8 +115,8 @@ configure(allprojects) { project ->
|
||||
dependency "net.sf.ehcache:ehcache:2.10.6"
|
||||
dependency "org.ehcache:jcache:1.0.1"
|
||||
dependency "org.ehcache:ehcache:3.4.0"
|
||||
dependency "org.hibernate:hibernate-core:5.4.22.Final"
|
||||
dependency "org.hibernate:hibernate-validator:6.1.6.Final"
|
||||
dependency "org.hibernate:hibernate-core:5.4.28.Final"
|
||||
dependency "org.hibernate:hibernate-validator:6.1.7.Final"
|
||||
dependency "org.webjars:webjars-locator-core:0.46"
|
||||
dependency "org.webjars:underscorejs:1.8.3"
|
||||
|
||||
@@ -147,7 +146,7 @@ configure(allprojects) { project ->
|
||||
entry 'okhttp'
|
||||
entry 'mockwebserver'
|
||||
}
|
||||
dependency("org.apache.httpcomponents:httpclient:4.5.12") {
|
||||
dependency("org.apache.httpcomponents:httpclient:4.5.13") {
|
||||
exclude group: "commons-logging", name: "commons-logging"
|
||||
}
|
||||
dependency("org.apache.httpcomponents:httpasyncclient:4.1.4") {
|
||||
@@ -180,7 +179,7 @@ configure(allprojects) { project ->
|
||||
dependency "org.testng:testng:6.14.3"
|
||||
dependency "org.hamcrest:hamcrest:2.1"
|
||||
dependency "org.awaitility:awaitility:3.1.6"
|
||||
dependency "org.assertj:assertj-core:3.17.2"
|
||||
dependency "org.assertj:assertj-core:3.18.1"
|
||||
dependencySet(group: 'org.xmlunit', version: '2.6.2') {
|
||||
entry 'xmlunit-assertj'
|
||||
entry('xmlunit-matchers') {
|
||||
@@ -193,7 +192,7 @@ configure(allprojects) { project ->
|
||||
}
|
||||
entry 'mockito-junit-jupiter'
|
||||
}
|
||||
dependency "io.mockk:mockk:1.10.0"
|
||||
dependency "io.mockk:mockk:1.10.2"
|
||||
|
||||
dependency("net.sourceforge.htmlunit:htmlunit:2.43.0") {
|
||||
exclude group: "commons-logging", name: "commons-logging"
|
||||
@@ -225,7 +224,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.6"
|
||||
dependency "joda-time:joda-time:2.10.10"
|
||||
dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.7"
|
||||
dependency "org.javamoney:moneta:1.3"
|
||||
|
||||
@@ -326,7 +325,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "8.36.2"
|
||||
toolVersion = "8.38"
|
||||
configDir = rootProject.file("src/checkstyle")
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
== Spring Framework Concourse pipeline
|
||||
|
||||
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
|
||||
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
|
||||
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.2.x[Spring Framework 5.2.x].
|
||||
|
||||
=== Setting up your development environment
|
||||
|
||||
If you're part of the Spring Framework project on GitHub, you can get access to CI management features.
|
||||
First, you need to go to https://ci.spring.io and install the client CLI for your platform (see bottom right of the screen).
|
||||
|
||||
You can then login with the instance using:
|
||||
|
||||
[source]
|
||||
----
|
||||
$ fly -t spring login -n spring-framework -c https://ci.spring.io
|
||||
----
|
||||
|
||||
Once logged in, you should get something like:
|
||||
|
||||
[source]
|
||||
----
|
||||
$ fly ts
|
||||
name url team expiry
|
||||
spring https://ci.spring.io spring-framework Wed, 25 Mar 2020 17:45:26 UTC
|
||||
----
|
||||
|
||||
=== Pipeline configuration and structure
|
||||
|
||||
The build pipelines are described in `pipeline.yml` file.
|
||||
|
||||
This file is listing Concourse resources, i.e. build inputs and outputs such as container images, artifact repositories, source repositories, notification services, etc.
|
||||
|
||||
It also describes jobs (a job is a sequence of inputs, tasks and outputs); jobs are organized by groups.
|
||||
|
||||
The `pipeline.yml` definition contains `((parameters))` which are loaded from the `parameters.yml` file or from our https://docs.cloudfoundry.org/credhub/[credhub instance].
|
||||
|
||||
You'll find in this folder the following resources:
|
||||
|
||||
* `pipeline.yml` the build pipeline
|
||||
* `parameters.yml` the build parameters used for the pipeline
|
||||
* `images/` holds the container images definitions used in this pipeline
|
||||
* `scripts/` holds the build scripts that ship within the CI container images
|
||||
* `tasks` contains the task definitions used in the main `pipeline.yml`
|
||||
|
||||
=== Updating the build pipeline
|
||||
|
||||
Updating files on the repository is not enough to update the build pipeline, as changes need to be applied.
|
||||
|
||||
The pipeline can be deployed using the following command:
|
||||
|
||||
[source]
|
||||
----
|
||||
$ fly -t spring set-pipeline -p spring-framework-5.2.x -c ci/pipeline.yml -l ci/parameters.yml
|
||||
----
|
||||
|
||||
NOTE: This assumes that you have credhub integration configured with the appropriate secrets.
|
||||
@@ -0,0 +1,17 @@
|
||||
changelog:
|
||||
repository: spring-projects/spring-framework
|
||||
sections:
|
||||
- title: ":star: New Features"
|
||||
labels:
|
||||
- "type: enhancement"
|
||||
- title: ":lady_beetle: Bug Fixes"
|
||||
labels:
|
||||
- "type: bug"
|
||||
- "type: regression"
|
||||
- title: ":notebook_with_decorative_cover: Documentation"
|
||||
labels:
|
||||
- "type: documentation"
|
||||
- title: ":hammer: Dependency Upgrades"
|
||||
sort: "title"
|
||||
labels:
|
||||
- "type: dependency-upgrade"
|
||||
@@ -0,0 +1,10 @@
|
||||
logging:
|
||||
level:
|
||||
io.spring.concourse: DEBUG
|
||||
spring:
|
||||
main:
|
||||
banner-mode: off
|
||||
sonatype:
|
||||
exclude:
|
||||
- 'build-info\.json'
|
||||
- '.*\.zip'
|
||||
@@ -0,0 +1,21 @@
|
||||
== CI Images
|
||||
|
||||
These images are used by CI to run the actual builds.
|
||||
|
||||
To build the image locally run the following from this directory:
|
||||
|
||||
----
|
||||
$ docker build --no-cache -f <image-folder>/Dockerfile .
|
||||
----
|
||||
|
||||
For example
|
||||
|
||||
----
|
||||
$ docker build --no-cache -f spring-framework-ci-image/Dockerfile .
|
||||
----
|
||||
|
||||
To test run:
|
||||
|
||||
----
|
||||
$ docker run -it --entrypoint /bin/bash <SHA>
|
||||
----
|
||||
@@ -0,0 +1,8 @@
|
||||
FROM ubuntu:focal-20220302
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
RUN ./setup.sh java11
|
||||
|
||||
ENV JAVA_HOME /opt/openjdk
|
||||
ENV PATH $JAVA_HOME/bin:$PATH
|
||||
@@ -0,0 +1,8 @@
|
||||
FROM ubuntu:focal-20220302
|
||||
|
||||
ADD setup.sh /setup.sh
|
||||
ADD get-jdk-url.sh /get-jdk-url.sh
|
||||
RUN ./setup.sh java8
|
||||
|
||||
ENV JAVA_HOME /opt/openjdk
|
||||
ENV PATH $JAVA_HOME/bin:$PATH
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
java8)
|
||||
echo "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u322-b06/OpenJDK8U-jdk_x64_linux_hotspot_8u322b06.tar.gz"
|
||||
;;
|
||||
java11)
|
||||
echo "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.14.1%2B1/OpenJDK11U-jdk_x64_linux_hotspot_11.0.14.1_1.tar.gz"
|
||||
;;
|
||||
*)
|
||||
echo $"Unknown java version"
|
||||
exit 1
|
||||
esac
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
set -ex
|
||||
|
||||
###########################################################
|
||||
# UTILS
|
||||
###########################################################
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install --no-install-recommends -y tzdata ca-certificates net-tools libxml2-utils git curl libudev1 libxml2-utils iptables iproute2 jq fontconfig
|
||||
ln -fs /usr/share/zoneinfo/UTC /etc/localtime
|
||||
dpkg-reconfigure --frontend noninteractive tzdata
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/concourse-java.sh > /opt/concourse-java.sh
|
||||
|
||||
curl --output /opt/concourse-release-scripts.jar https://repo.spring.io/release/io/spring/concourse/releasescripts/concourse-release-scripts/0.3.2/concourse-release-scripts-0.3.2.jar
|
||||
|
||||
###########################################################
|
||||
# JAVA
|
||||
###########################################################
|
||||
JDK_URL=$( ./get-jdk-url.sh $1 )
|
||||
|
||||
mkdir -p /opt/openjdk
|
||||
cd /opt/openjdk
|
||||
curl -L ${JDK_URL} | tar zx --strip-components=1
|
||||
test -f /opt/openjdk/bin/java
|
||||
test -f /opt/openjdk/bin/javac
|
||||
|
||||
###########################################################
|
||||
# GRADLE ENTERPRISE
|
||||
###########################################################
|
||||
cd /
|
||||
mkdir ~/.gradle
|
||||
echo 'systemProp.user.name=concourse' > ~/.gradle/gradle.properties
|
||||
@@ -0,0 +1,13 @@
|
||||
email-server: "smtp.svc.pivotal.io"
|
||||
email-from: "ci@spring.io"
|
||||
email-to: ["spring-framework-dev@pivotal.io"]
|
||||
github-repo: "https://github.com/spring-projects/spring-framework.git"
|
||||
github-repo-name: "spring-projects/spring-framework"
|
||||
docker-hub-organization: "springci"
|
||||
artifactory-server: "https://repo.spring.io"
|
||||
branch: "5.2.x"
|
||||
milestone: "5.2.x"
|
||||
build-name: "spring-framework"
|
||||
pipeline-name: "spring-framework"
|
||||
concourse-url: "https://ci.spring.io"
|
||||
task-timeout: 1h00m
|
||||
+402
@@ -0,0 +1,402 @@
|
||||
anchors:
|
||||
git-repo-resource-source: &git-repo-resource-source
|
||||
uri: ((github-repo))
|
||||
username: ((github-username))
|
||||
password: ((github-ci-release-token))
|
||||
branch: ((branch))
|
||||
gradle-enterprise-task-params: &gradle-enterprise-task-params
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
|
||||
sonatype-task-params: &sonatype-task-params
|
||||
SONATYPE_USERNAME: ((sonatype-username))
|
||||
SONATYPE_PASSWORD: ((sonatype-password))
|
||||
SONATYPE_URL: ((sonatype-url))
|
||||
SONATYPE_STAGING_PROFILE_ID: ((sonatype-staging-profile-id))
|
||||
artifactory-task-params: &artifactory-task-params
|
||||
ARTIFACTORY_SERVER: ((artifactory-server))
|
||||
ARTIFACTORY_USERNAME: ((artifactory-username))
|
||||
ARTIFACTORY_PASSWORD: ((artifactory-password))
|
||||
build-project-task-params: &build-project-task-params
|
||||
privileged: true
|
||||
timeout: ((task-timeout))
|
||||
params:
|
||||
BRANCH: ((branch))
|
||||
<<: *gradle-enterprise-task-params
|
||||
docker-resource-source: &docker-resource-source
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
slack-fail-params: &slack-fail-params
|
||||
text: >
|
||||
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
|
||||
[$TEXT_FILE_CONTENT]
|
||||
text_file: git-repo/build/build-scan-uri.txt
|
||||
silent: true
|
||||
icon_emoji: ":concourse:"
|
||||
username: concourse-ci
|
||||
changelog-task-params: &changelog-task-params
|
||||
name: generated-changelog/tag
|
||||
tag: generated-changelog/tag
|
||||
body: generated-changelog/changelog.md
|
||||
github-task-params: &github-task-params
|
||||
GITHUB_USERNAME: ((github-username))
|
||||
GITHUB_TOKEN: ((github-ci-release-token))
|
||||
|
||||
resource_types:
|
||||
- name: registry-image
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/registry-image-resource
|
||||
tag: 1.7.1
|
||||
- name: artifactory-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: springio/artifactory-resource
|
||||
tag: 0.0.17
|
||||
- name: github-release
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: concourse/github-release-resource
|
||||
tag: 1.5.5
|
||||
- name: github-status-resource
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
- name: slack-notification
|
||||
type: registry-image
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
resources:
|
||||
- name: git-repo
|
||||
type: git
|
||||
icon: github
|
||||
source:
|
||||
<<: *git-repo-resource-source
|
||||
- name: every-morning
|
||||
type: time
|
||||
icon: alarm
|
||||
source:
|
||||
start: 8:00 AM
|
||||
stop: 9:00 AM
|
||||
location: Europe/Vienna
|
||||
- name: ci-images-git-repo
|
||||
type: git
|
||||
icon: github
|
||||
source:
|
||||
uri: ((github-repo))
|
||||
branch: ((branch))
|
||||
paths: ["ci/images/*"]
|
||||
- name: ci-image
|
||||
type: docker-image
|
||||
icon: docker
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-ci
|
||||
tag: ((milestone))
|
||||
- name: ci-image-jdk11
|
||||
type: docker-image
|
||||
icon: docker
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-ci-jdk11
|
||||
tag: ((milestone))
|
||||
- name: artifactory-repo
|
||||
type: artifactory-resource
|
||||
icon: package-variant
|
||||
source:
|
||||
uri: ((artifactory-server))
|
||||
username: ((artifactory-username))
|
||||
password: ((artifactory-password))
|
||||
build_name: ((build-name))
|
||||
- name: repo-status-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: build
|
||||
- name: repo-status-jdk11-build
|
||||
type: github-status-resource
|
||||
icon: eye-check-outline
|
||||
source:
|
||||
repository: ((github-repo-name))
|
||||
access_token: ((github-ci-status-token))
|
||||
branch: ((branch))
|
||||
context: jdk11-build
|
||||
- name: slack-alert
|
||||
type: slack-notification
|
||||
icon: slack
|
||||
source:
|
||||
url: ((slack-webhook-url))
|
||||
- name: github-pre-release
|
||||
type: github-release
|
||||
icon: briefcase-download-outline
|
||||
source:
|
||||
owner: spring-projects
|
||||
repository: spring-framework
|
||||
access_token: ((github-ci-release-token))
|
||||
pre_release: true
|
||||
release: false
|
||||
- name: github-release
|
||||
type: github-release
|
||||
icon: briefcase-download
|
||||
source:
|
||||
owner: spring-projects
|
||||
repository: spring-framework
|
||||
access_token: ((github-ci-release-token))
|
||||
pre_release: false
|
||||
jobs:
|
||||
- name: build-ci-images
|
||||
plan:
|
||||
- get: ci-images-git-repo
|
||||
trigger: true
|
||||
- in_parallel:
|
||||
- put: ci-image
|
||||
params:
|
||||
build: ci-images-git-repo/ci/images
|
||||
dockerfile: ci-images-git-repo/ci/images/ci-image/Dockerfile
|
||||
- put: ci-image-jdk11
|
||||
params:
|
||||
build: ci-images-git-repo/ci/images
|
||||
dockerfile: ci-images-git-repo/ci/images/ci-image-jdk11/Dockerfile
|
||||
- name: build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: true
|
||||
- put: repo-status-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: build-project
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/build-project.yml
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- put: artifactory-repo
|
||||
params: &artifactory-params
|
||||
signing_key: ((signing-key))
|
||||
signing_passphrase: ((signing-passphrase))
|
||||
repo: libs-snapshot-local
|
||||
folder: distribution-repository
|
||||
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
|
||||
build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}"
|
||||
disable_checksum_uploads: true
|
||||
threads: 8
|
||||
artifact_set:
|
||||
- include:
|
||||
- "/**/spring-*.zip"
|
||||
properties:
|
||||
"zip.name": "spring-framework"
|
||||
"zip.displayname": "Spring Framework"
|
||||
"zip.deployed": "false"
|
||||
- include:
|
||||
- "/**/spring-*-docs.zip"
|
||||
properties:
|
||||
"zip.type": "docs"
|
||||
- include:
|
||||
- "/**/spring-*-dist.zip"
|
||||
properties:
|
||||
"zip.type": "dist"
|
||||
- include:
|
||||
- "/**/spring-*-schema.zip"
|
||||
properties:
|
||||
"zip.type": "schema"
|
||||
get_params:
|
||||
threads: 8
|
||||
- name: jdk11-build
|
||||
serial: true
|
||||
public: true
|
||||
plan:
|
||||
- get: ci-image-jdk11
|
||||
- get: git-repo
|
||||
- get: every-morning
|
||||
trigger: true
|
||||
- put: repo-status-jdk11-build
|
||||
params: { state: "pending", commit: "git-repo" }
|
||||
- do:
|
||||
- task: check-project
|
||||
image: ci-image-jdk11
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
<<: *build-project-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk11-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk11-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: stage-milestone
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-milestone
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
trigger: false
|
||||
passed: [stage-milestone]
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *artifactory-task-params
|
||||
- task: generate-changelog
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
<<: *github-task-params
|
||||
- put: github-pre-release
|
||||
params:
|
||||
<<: *changelog-task-params
|
||||
- name: stage-rc
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-rc
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
trigger: false
|
||||
passed: [stage-rc]
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
<<: *artifactory-task-params
|
||||
- task: generate-changelog
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
<<: *github-task-params
|
||||
- put: github-pre-release
|
||||
params:
|
||||
<<: *changelog-task-params
|
||||
- name: stage-release
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
repo: libs-staging-local
|
||||
- put: git-repo
|
||||
params:
|
||||
repository: stage-git-repo
|
||||
- name: promote-release
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
trigger: false
|
||||
passed: [stage-release]
|
||||
params:
|
||||
download_artifacts: true
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
image: ci-image
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *artifactory-task-params
|
||||
<<: *sonatype-task-params
|
||||
- name: create-github-release
|
||||
serial: true
|
||||
plan:
|
||||
- get: ci-image
|
||||
- get: git-repo
|
||||
- get: artifactory-repo
|
||||
trigger: true
|
||||
passed: [promote-release]
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: generate-changelog
|
||||
file: git-repo/ci/tasks/generate-changelog.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *github-task-params
|
||||
- put: github-release
|
||||
params:
|
||||
<<: *changelog-task-params
|
||||
|
||||
groups:
|
||||
- name: "builds"
|
||||
jobs: ["build", "jdk11-build"]
|
||||
- name: "releases"
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
|
||||
- name: "ci-images"
|
||||
jobs: ["build-ci-images"]
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
repository=$(pwd)/distribution-repository
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
|
||||
popd > /dev/null
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 check
|
||||
popd > /dev/null
|
||||
@@ -0,0 +1,2 @@
|
||||
source /opt/concourse-java.sh
|
||||
setup_symlinks
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
CONFIG_DIR=git-repo/ci/config
|
||||
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
|
||||
|
||||
milestone=${version}
|
||||
if [[ $RELEASE_TYPE = "RELEASE" ]]; then
|
||||
milestone=${version%.RELEASE}
|
||||
fi
|
||||
|
||||
java -jar /github-changelog-generator.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/changelog-generator.yml \
|
||||
${milestone} generated-changelog/changelog.md
|
||||
|
||||
echo ${version} > generated-changelog/version
|
||||
echo v${version} > generated-changelog/tag
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
CONFIG_DIR=git-repo/ci/config
|
||||
|
||||
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
|
||||
export BUILD_INFO_LOCATION=$(pwd)/artifactory-repo/build-info.json
|
||||
|
||||
java -jar /opt/concourse-release-scripts.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
|
||||
publishToCentral $RELEASE_TYPE $BUILD_INFO_LOCATION artifactory-repo || { exit 1; }
|
||||
|
||||
java -jar /opt/concourse-release-scripts.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
|
||||
promote $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
|
||||
|
||||
echo "Promotion complete"
|
||||
echo $version > version/version
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source $(dirname $0)/common.sh
|
||||
repository=$(pwd)/distribution-repository
|
||||
|
||||
pushd git-repo > /dev/null
|
||||
git fetch --tags --all > /dev/null
|
||||
popd > /dev/null
|
||||
|
||||
git clone git-repo stage-git-repo > /dev/null
|
||||
|
||||
pushd stage-git-repo > /dev/null
|
||||
|
||||
snapshotVersion=$( awk -F '=' '$1 == "version" { print $2 }' gradle.properties )
|
||||
if [[ $RELEASE_TYPE = "M" ]]; then
|
||||
stageVersion=$( get_next_milestone_release $snapshotVersion)
|
||||
nextVersion=$snapshotVersion
|
||||
elif [[ $RELEASE_TYPE = "RC" ]]; then
|
||||
stageVersion=$( get_next_rc_release $snapshotVersion)
|
||||
nextVersion=$snapshotVersion
|
||||
elif [[ $RELEASE_TYPE = "RELEASE" ]]; then
|
||||
stageVersion=$( get_next_release $snapshotVersion "RELEASE")
|
||||
nextVersion=$( bump_version_number $snapshotVersion)
|
||||
else
|
||||
echo "Unknown release type $RELEASE_TYPE" >&2; exit 1;
|
||||
fi
|
||||
|
||||
echo "Staging $stageVersion (next version will be $nextVersion)"
|
||||
sed -i "s/version=$snapshotVersion/version=$stageVersion/" gradle.properties
|
||||
|
||||
git config user.name "Spring Builds" > /dev/null
|
||||
git config user.email "spring-builds@users.noreply.github.com" > /dev/null
|
||||
git add gradle.properties > /dev/null
|
||||
git commit -m"Release v$stageVersion" > /dev/null
|
||||
git tag -a "v$stageVersion" -m"Release v$stageVersion" > /dev/null
|
||||
|
||||
./gradlew --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository
|
||||
|
||||
git reset --hard HEAD^ > /dev/null
|
||||
if [[ $nextVersion != $snapshotVersion ]]; then
|
||||
echo "Setting next development version (v$nextVersion)"
|
||||
sed -i "s/version=$snapshotVersion/version=$nextVersion/" gradle.properties
|
||||
git add gradle.properties > /dev/null
|
||||
git commit -m"Next development version (v$nextVersion)" > /dev/null
|
||||
fi;
|
||||
|
||||
echo "Staging Complete"
|
||||
|
||||
popd > /dev/null
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
export BUILD_INFO_LOCATION=$(pwd)/artifactory-repo/build-info.json
|
||||
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
|
||||
java -jar /opt/concourse-release-scripts.jar syncToCentral "RELEASE" $BUILD_INFO_LOCATION || { exit 1; }
|
||||
|
||||
echo "Sync complete"
|
||||
echo $version > version/version
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/build-project.sh
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: distribution-repository
|
||||
- name: git-repo
|
||||
caches:
|
||||
- path: gradle
|
||||
params:
|
||||
BRANCH:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
run:
|
||||
path: bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
${PWD}/git-repo/ci/scripts/check-project.sh
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
---
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/github-changelog-generator
|
||||
tag: '0.0.7'
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
outputs:
|
||||
- name: generated-changelog
|
||||
params:
|
||||
GITHUB_ORGANIZATION:
|
||||
GITHUB_REPO:
|
||||
GITHUB_USERNAME:
|
||||
GITHUB_TOKEN:
|
||||
RELEASE_TYPE:
|
||||
run:
|
||||
path: git-repo/ci/scripts/generate-changelog.sh
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
outputs:
|
||||
- name: version
|
||||
params:
|
||||
RELEASE_TYPE:
|
||||
ARTIFACTORY_SERVER:
|
||||
ARTIFACTORY_USERNAME:
|
||||
ARTIFACTORY_PASSWORD:
|
||||
SONATYPE_USER:
|
||||
SONATYPE_PASSWORD:
|
||||
SONATYPE_URL:
|
||||
SONATYPE_STAGING_PROFILE_ID:
|
||||
run:
|
||||
path: git-repo/ci/scripts/promote-version.sh
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
outputs:
|
||||
- name: stage-git-repo
|
||||
- name: distribution-repository
|
||||
params:
|
||||
RELEASE_TYPE:
|
||||
CI: true
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME:
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD:
|
||||
GRADLE_ENTERPRISE_URL: https://ge.spring.io
|
||||
caches:
|
||||
- path: gradle
|
||||
run:
|
||||
path: git-repo/ci/scripts/stage-version.sh
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.2.11.BUILD-SNAPSHOT
|
||||
version=5.2.23.RELEASE
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
@@ -103,9 +103,6 @@ dokka {
|
||||
externalDocumentationLink {
|
||||
url = new URL("https://www.reactive-streams.org/reactive-streams-1.0.1-javadoc/")
|
||||
}
|
||||
externalDocumentationLink {
|
||||
url = new URL("https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -29,12 +29,11 @@ eclipse.classpath.file.whenMerged { classpath ->
|
||||
classpath.entries.removeAll { entry -> (entry.path =~ /(?!.*?repack.*\.jar).*?\/([^\/]+)\/build\/libs\/[^\/]+\.jar/) }
|
||||
}
|
||||
|
||||
|
||||
// Use separate main/test outputs (prevents WTP from packaging test classes)
|
||||
eclipse.classpath.defaultOutputDir = file(project.name+"/bin/eclipse")
|
||||
eclipse.classpath.file.beforeMerged { classpath ->
|
||||
classpath.entries.findAll{ it instanceof SourceFolder }.each {
|
||||
if(it.output.startsWith("bin/")) {
|
||||
if (it.output.startsWith("bin/")) {
|
||||
it.output = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
apply plugin: "maven-publish"
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
@@ -50,6 +49,16 @@ publishing {
|
||||
}
|
||||
}
|
||||
|
||||
artifactoryPublish {
|
||||
publications(publishing.publications.mavenJava)
|
||||
configureDeploymentRepository(project)
|
||||
|
||||
void configureDeploymentRepository(Project project) {
|
||||
project.plugins.withType(MavenPublishPlugin.class).all {
|
||||
PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
|
||||
if (project.hasProperty("deploymentRepository")) {
|
||||
publishing.repositories.maven {
|
||||
it.url = project.property("deploymentRepository")
|
||||
it.name = "deployment"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -10,8 +10,8 @@ include "spring-aop"
|
||||
include "spring-aspects"
|
||||
include "spring-beans"
|
||||
include "spring-context"
|
||||
include "spring-context-support"
|
||||
include "spring-context-indexer"
|
||||
include "spring-context-support"
|
||||
include "spring-core"
|
||||
include "kotlin-coroutines"
|
||||
project(':kotlin-coroutines').projectDir = file('spring-core/kotlin-coroutines')
|
||||
@@ -26,8 +26,8 @@ include "spring-oxm"
|
||||
include "spring-test"
|
||||
include "spring-tx"
|
||||
include "spring-web"
|
||||
include "spring-webmvc"
|
||||
include "spring-webflux"
|
||||
include "spring-webmvc"
|
||||
include "spring-websocket"
|
||||
include "framework-bom"
|
||||
include "integration-tests"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -23,7 +23,7 @@ import java.lang.reflect.AccessibleObject;
|
||||
* terminology).
|
||||
*
|
||||
* <p>A runtime joinpoint is an <i>event</i> that occurs on a static
|
||||
* joinpoint (i.e. a location in a the program). For instance, an
|
||||
* joinpoint (i.e. a location in a program). For instance, an
|
||||
* invocation is the runtime joinpoint on a method (static joinpoint).
|
||||
* The static part of a given joinpoint can be generically retrieved
|
||||
* using the {@link #getStaticPart()} method.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2021 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,7 +26,7 @@ import org.aopalliance.aop.Advice;
|
||||
* <p>Introductions are often <b>mixins</b>, enabling the building of composite
|
||||
* objects that can achieve many of the goals of multiple inheritance in Java.
|
||||
*
|
||||
* <p>Compared to {qlink IntroductionInfo}, this interface allows an advice to
|
||||
* <p>Compared to {@link IntroductionInfo}, this interface allows an advice to
|
||||
* implement a range of interfaces that is not necessarily known in advance.
|
||||
* Thus an {@link IntroductionAdvisor} can be used to specify which interfaces
|
||||
* will be exposed in an advised object.
|
||||
|
||||
+6
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -219,10 +219,12 @@ public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint,
|
||||
@Override
|
||||
@Nullable
|
||||
public String[] getParameterNames() {
|
||||
if (this.parameterNames == null) {
|
||||
this.parameterNames = parameterNameDiscoverer.getParameterNames(getMethod());
|
||||
String[] parameterNames = this.parameterNames;
|
||||
if (parameterNames == null) {
|
||||
parameterNames = parameterNameDiscoverer.getParameterNames(getMethod());
|
||||
this.parameterNames = parameterNames;
|
||||
}
|
||||
return this.parameterNames;
|
||||
return parameterNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -103,10 +103,11 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
|
||||
@Override
|
||||
public void validate(Class<?> aspectClass) throws AopConfigException {
|
||||
// If the parent has the annotation and isn't abstract it's an error
|
||||
if (aspectClass.getSuperclass().getAnnotation(Aspect.class) != null &&
|
||||
!Modifier.isAbstract(aspectClass.getSuperclass().getModifiers())) {
|
||||
Class<?> superclass = aspectClass.getSuperclass();
|
||||
if (superclass.getAnnotation(Aspect.class) != null &&
|
||||
!Modifier.isAbstract(superclass.getModifiers())) {
|
||||
throw new AopConfigException("[" + aspectClass.getName() + "] cannot extend concrete aspect [" +
|
||||
aspectClass.getSuperclass().getName() + "]");
|
||||
superclass.getName() + "]");
|
||||
}
|
||||
|
||||
AjType<?> ajType = AjTypeSystem.getAjType(aspectClass);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -49,6 +49,7 @@ import org.springframework.cglib.proxy.Factory;
|
||||
import org.springframework.cglib.proxy.MethodInterceptor;
|
||||
import org.springframework.cglib.proxy.MethodProxy;
|
||||
import org.springframework.cglib.proxy.NoOp;
|
||||
import org.springframework.core.KotlinDetector;
|
||||
import org.springframework.core.SmartClassLoader;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -236,7 +237,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
* validates it if not.
|
||||
*/
|
||||
private void validateClassIfNecessary(Class<?> proxySuperClass, @Nullable ClassLoader proxyClassLoader) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
synchronized (validatedClasses) {
|
||||
if (!validatedClasses.containsKey(proxySuperClass)) {
|
||||
doValidateClass(proxySuperClass, proxyClassLoader,
|
||||
@@ -374,6 +375,22 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the given method with a CGLIB MethodProxy if possible, falling back
|
||||
* to a plain reflection invocation in case of a fast-class generation failure.
|
||||
*/
|
||||
@Nullable
|
||||
private static Object invokeMethod(@Nullable Object target, Method method, Object[] args, MethodProxy methodProxy)
|
||||
throws Throwable {
|
||||
try {
|
||||
return methodProxy.invoke(target, args);
|
||||
}
|
||||
catch (CodeGenerationException ex) {
|
||||
CglibMethodInvocation.logFastClassGenerationFailure(method);
|
||||
return AopUtils.invokeJoinpointUsingReflection(target, method, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a return value. Wraps a return of {@code this} if necessary to be the
|
||||
* {@code proxy} and also verifies that {@code null} is not returned as a primitive.
|
||||
@@ -424,7 +441,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
@Override
|
||||
@Nullable
|
||||
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
|
||||
Object retVal = methodProxy.invoke(this.target, args);
|
||||
Object retVal = invokeMethod(this.target, method, args, methodProxy);
|
||||
return processReturnType(proxy, this.target, method, retVal);
|
||||
}
|
||||
}
|
||||
@@ -449,7 +466,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
Object oldProxy = null;
|
||||
try {
|
||||
oldProxy = AopContext.setCurrentProxy(proxy);
|
||||
Object retVal = methodProxy.invoke(this.target, args);
|
||||
Object retVal = invokeMethod(this.target, method, args, methodProxy);
|
||||
return processReturnType(proxy, this.target, method, retVal);
|
||||
}
|
||||
finally {
|
||||
@@ -477,7 +494,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
|
||||
Object target = this.targetSource.getTarget();
|
||||
try {
|
||||
Object retVal = methodProxy.invoke(target, args);
|
||||
Object retVal = invokeMethod(target, method, args, methodProxy);
|
||||
return processReturnType(proxy, target, method, retVal);
|
||||
}
|
||||
finally {
|
||||
@@ -507,7 +524,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
Object target = this.targetSource.getTarget();
|
||||
try {
|
||||
oldProxy = AopContext.setCurrentProxy(proxy);
|
||||
Object retVal = methodProxy.invoke(target, args);
|
||||
Object retVal = invokeMethod(target, method, args, methodProxy);
|
||||
return processReturnType(proxy, target, method, retVal);
|
||||
}
|
||||
finally {
|
||||
@@ -678,13 +695,13 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
Object retVal;
|
||||
// Check whether we only have one InvokerInterceptor: that is,
|
||||
// no real advice, but just reflective invocation of the target.
|
||||
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
|
||||
if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
|
||||
// We can skip creating a MethodInvocation: just invoke the target directly.
|
||||
// Note that the final invoker must be an InvokerInterceptor, so we know
|
||||
// it does nothing but a reflective operation on the target, and no hot
|
||||
// swapping or fancy proxying.
|
||||
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
|
||||
retVal = methodProxy.invoke(target, argsToUse);
|
||||
retVal = invokeMethod(target, method, argsToUse, methodProxy);
|
||||
}
|
||||
else {
|
||||
// We need to create a method invocation...
|
||||
@@ -736,10 +753,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
|
||||
|
||||
// Only use method proxy for public methods not derived from java.lang.Object
|
||||
this.methodProxy = (Modifier.isPublic(method.getModifiers()) &&
|
||||
method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
|
||||
!AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method) ?
|
||||
methodProxy : null);
|
||||
this.methodProxy = (isMethodProxyCompatible(method) ? methodProxy : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -752,10 +766,17 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (ReflectionUtils.declaresException(getMethod(), ex.getClass())) {
|
||||
if (ReflectionUtils.declaresException(getMethod(), ex.getClass()) ||
|
||||
KotlinDetector.isKotlinType(getMethod().getDeclaringClass())) {
|
||||
// Propagate original exception if declared on the target method
|
||||
// (with callers expecting it). Always propagate it for Kotlin code
|
||||
// since checked exceptions do not have to be explicitly declared there.
|
||||
throw ex;
|
||||
}
|
||||
else {
|
||||
// Checked exception thrown in the interceptor but not declared on the
|
||||
// target method signature -> apply an UndeclaredThrowableException,
|
||||
// aligned with standard JDK dynamic proxy behavior.
|
||||
throw new UndeclaredThrowableException(ex);
|
||||
}
|
||||
}
|
||||
@@ -768,10 +789,25 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
@Override
|
||||
protected Object invokeJoinpoint() throws Throwable {
|
||||
if (this.methodProxy != null) {
|
||||
return this.methodProxy.invoke(this.target, this.arguments);
|
||||
try {
|
||||
return this.methodProxy.invoke(this.target, this.arguments);
|
||||
}
|
||||
catch (CodeGenerationException ex) {
|
||||
logFastClassGenerationFailure(this.method);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return super.invokeJoinpoint();
|
||||
return super.invokeJoinpoint();
|
||||
}
|
||||
|
||||
static boolean isMethodProxyCompatible(Method method) {
|
||||
return (Modifier.isPublic(method.getModifiers()) &&
|
||||
method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
|
||||
!AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method));
|
||||
}
|
||||
|
||||
static void logFastClassGenerationFailure(Method method) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Failed to generate CGLIB fast class for method: " + method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2021 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,9 +43,11 @@ import org.springframework.aop.SpringProxy;
|
||||
* @see AdvisedSupport#setProxyTargetClass
|
||||
* @see AdvisedSupport#setInterfaces
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 7930414337282325166L;
|
||||
|
||||
|
||||
@Override
|
||||
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
|
||||
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -421,11 +421,7 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
|
||||
* are unaffected by such changes.
|
||||
*/
|
||||
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
|
||||
if (this.advisorChainInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ObjectUtils.isEmpty(this.interceptorNames)) {
|
||||
if (!this.advisorChainInitialized && !ObjectUtils.isEmpty(this.interceptorNames)) {
|
||||
if (this.beanFactory == null) {
|
||||
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
|
||||
"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
|
||||
@@ -464,9 +460,9 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
|
||||
addAdvisorOnChainCreation(advice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.advisorChainInitialized = true;
|
||||
this.advisorChainInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+17
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.aop.framework.autoproxy;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -449,7 +450,17 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
|
||||
ProxyFactory proxyFactory = new ProxyFactory();
|
||||
proxyFactory.copyFrom(this);
|
||||
|
||||
if (!proxyFactory.isProxyTargetClass()) {
|
||||
if (proxyFactory.isProxyTargetClass()) {
|
||||
// Explicit handling of JDK proxy targets (for introduction advice scenarios)
|
||||
if (Proxy.isProxyClass(beanClass)) {
|
||||
// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
|
||||
for (Class<?> ifc : beanClass.getInterfaces()) {
|
||||
proxyFactory.addInterface(ifc);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No proxyTargetClass flag enforced, let's apply our default checks...
|
||||
if (shouldProxyTargetClass(beanClass, beanName)) {
|
||||
proxyFactory.setProxyTargetClass(true);
|
||||
}
|
||||
@@ -513,7 +524,10 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
|
||||
|
||||
List<Object> allInterceptors = new ArrayList<>();
|
||||
if (specificInterceptors != null) {
|
||||
allInterceptors.addAll(Arrays.asList(specificInterceptors));
|
||||
if (specificInterceptors.length > 0) {
|
||||
// specificInterceptors may equals PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS
|
||||
allInterceptors.addAll(Arrays.asList(specificInterceptors));
|
||||
}
|
||||
if (commonInterceptors.length > 0) {
|
||||
if (this.applyCommonInterceptorsFirst) {
|
||||
allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -71,7 +71,8 @@ public class DefaultIntroductionAdvisor implements IntroductionAdvisor, ClassFil
|
||||
if (introductionInfo != null) {
|
||||
Class<?>[] introducedInterfaces = introductionInfo.getInterfaces();
|
||||
if (introducedInterfaces.length == 0) {
|
||||
throw new IllegalArgumentException("IntroductionAdviceSupport implements no interfaces");
|
||||
throw new IllegalArgumentException(
|
||||
"IntroductionInfo defines no interfaces to introduce: " + introductionInfo);
|
||||
}
|
||||
for (Class<?> ifc : introducedInterfaces) {
|
||||
addInterface(ifc);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -183,7 +183,7 @@ public class ProxyFactoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetsAllInterfaces() throws Exception {
|
||||
public void testGetsAllInterfaces() {
|
||||
// Extend to get new interface
|
||||
class TestBeanSubclass extends TestBean implements Comparable<Object> {
|
||||
@Override
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -59,7 +59,8 @@ public abstract aspect AbstractTransactionAspect extends TransactionAspectSuppor
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
clearTransactionManagerCache(); // An aspect is basically a singleton
|
||||
// An aspect is basically a singleton -> cleanup on destruction
|
||||
clearTransactionManagerCache();
|
||||
}
|
||||
|
||||
@SuppressAjWarnings("adviceDidNotMatch")
|
||||
|
||||
+8
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,13 +22,18 @@ import org.springframework.contextsupport.testfixture.jcache.AbstractJCacheAnnot
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class JCacheAspectJNamespaceConfigTests extends AbstractJCacheAnnotationTests {
|
||||
|
||||
@Override
|
||||
protected ApplicationContext getApplicationContext() {
|
||||
return new GenericXmlApplicationContext(
|
||||
"/org/springframework/cache/config/annotation-jcache-aspectj.xml");
|
||||
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
|
||||
// Disallow bean definition overriding to test https://github.com/spring-projects/spring-framework/pull/27499
|
||||
context.setAllowBeanDefinitionOverriding(false);
|
||||
context.load("/org/springframework/cache/config/annotation-jcache-aspectj.xml");
|
||||
context.refresh();
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -305,8 +305,10 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||
Class<?> componentType = propValue.getClass().getComponentType();
|
||||
Object newArray = Array.newInstance(componentType, arrayIndex + 1);
|
||||
System.arraycopy(propValue, 0, newArray, 0, length);
|
||||
setPropertyValue(tokens.actualName, newArray);
|
||||
propValue = getPropertyValue(tokens.actualName);
|
||||
int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
|
||||
String propName = tokens.canonicalName.substring(0, lastKeyIndex);
|
||||
setPropertyValue(propName, newArray);
|
||||
propValue = getPropertyValue(propName);
|
||||
}
|
||||
Array.set(propValue, arrayIndex, convertedValue);
|
||||
}
|
||||
@@ -422,9 +424,12 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
throw createNotWritablePropertyException(tokens.canonicalName);
|
||||
if (this.suppressNotWritablePropertyException) {
|
||||
// Optimization for common ignoreUnknown=true scenario since the
|
||||
// exception would be caught and swallowed higher up anyway...
|
||||
return;
|
||||
}
|
||||
throw createNotWritablePropertyException(tokens.canonicalName);
|
||||
}
|
||||
|
||||
Object oldValue = null;
|
||||
@@ -806,7 +811,6 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||
* @param propertyPath property path, which may be nested
|
||||
* @return a property accessor for the target bean
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // avoid nested generic
|
||||
protected AbstractNestablePropertyAccessor getPropertyAccessorForPropertyPath(String propertyPath) {
|
||||
int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
|
||||
// Handle nested properties recursively.
|
||||
|
||||
+33
-20
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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,6 +40,8 @@ public abstract class AbstractPropertyAccessor extends TypeConverterSupport impl
|
||||
|
||||
private boolean autoGrowNestedPaths = false;
|
||||
|
||||
boolean suppressNotWritablePropertyException = false;
|
||||
|
||||
|
||||
@Override
|
||||
public void setExtractOldValueForEditor(boolean extractOldValueForEditor) {
|
||||
@@ -89,30 +91,41 @@ public abstract class AbstractPropertyAccessor extends TypeConverterSupport impl
|
||||
List<PropertyAccessException> propertyAccessExceptions = null;
|
||||
List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
|
||||
((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
|
||||
for (PropertyValue pv : propertyValues) {
|
||||
try {
|
||||
// This method may throw any BeansException, which won't be caught
|
||||
|
||||
if (ignoreUnknown) {
|
||||
this.suppressNotWritablePropertyException = true;
|
||||
}
|
||||
try {
|
||||
for (PropertyValue pv : propertyValues) {
|
||||
// setPropertyValue may throw any BeansException, which won't be caught
|
||||
// here, if there is a critical failure such as no matching field.
|
||||
// We can attempt to deal only with less serious exceptions.
|
||||
setPropertyValue(pv);
|
||||
}
|
||||
catch (NotWritablePropertyException ex) {
|
||||
if (!ignoreUnknown) {
|
||||
throw ex;
|
||||
try {
|
||||
setPropertyValue(pv);
|
||||
}
|
||||
// Otherwise, just ignore it and continue...
|
||||
}
|
||||
catch (NullValueInNestedPathException ex) {
|
||||
if (!ignoreInvalid) {
|
||||
throw ex;
|
||||
catch (NotWritablePropertyException ex) {
|
||||
if (!ignoreUnknown) {
|
||||
throw ex;
|
||||
}
|
||||
// Otherwise, just ignore it and continue...
|
||||
}
|
||||
// Otherwise, just ignore it and continue...
|
||||
}
|
||||
catch (PropertyAccessException ex) {
|
||||
if (propertyAccessExceptions == null) {
|
||||
propertyAccessExceptions = new ArrayList<>();
|
||||
catch (NullValueInNestedPathException ex) {
|
||||
if (!ignoreInvalid) {
|
||||
throw ex;
|
||||
}
|
||||
// Otherwise, just ignore it and continue...
|
||||
}
|
||||
propertyAccessExceptions.add(ex);
|
||||
catch (PropertyAccessException ex) {
|
||||
if (propertyAccessExceptions == null) {
|
||||
propertyAccessExceptions = new ArrayList<>();
|
||||
}
|
||||
propertyAccessExceptions.add(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (ignoreUnknown) {
|
||||
this.suppressNotWritablePropertyException = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,10 @@ public abstract class BeanUtils {
|
||||
values.put(byte.class, (byte) 0);
|
||||
values.put(short.class, (short) 0);
|
||||
values.put(int.class, 0);
|
||||
values.put(long.class, (long) 0);
|
||||
values.put(long.class, 0L);
|
||||
values.put(float.class, 0F);
|
||||
values.put(double.class, 0D);
|
||||
values.put(char.class, '\0');
|
||||
DEFAULT_TYPE_VALUES = Collections.unmodifiableMap(values);
|
||||
}
|
||||
|
||||
@@ -506,6 +509,7 @@ public abstract class BeanUtils {
|
||||
if (targetType == null || targetType.isArray() || unknownEditorTypes.contains(targetType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ClassLoader cl = targetType.getClassLoader();
|
||||
if (cl == null) {
|
||||
try {
|
||||
@@ -522,28 +526,34 @@ public abstract class BeanUtils {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String targetTypeName = targetType.getName();
|
||||
String editorName = targetTypeName + "Editor";
|
||||
try {
|
||||
Class<?> editorClass = cl.loadClass(editorName);
|
||||
if (!PropertyEditor.class.isAssignableFrom(editorClass)) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Editor class [" + editorName +
|
||||
"] does not implement [java.beans.PropertyEditor] interface");
|
||||
if (editorClass != null) {
|
||||
if (!PropertyEditor.class.isAssignableFrom(editorClass)) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Editor class [" + editorName +
|
||||
"] does not implement [java.beans.PropertyEditor] interface");
|
||||
}
|
||||
unknownEditorTypes.add(targetType);
|
||||
return null;
|
||||
}
|
||||
unknownEditorTypes.add(targetType);
|
||||
return null;
|
||||
return (PropertyEditor) instantiateClass(editorClass);
|
||||
}
|
||||
return (PropertyEditor) instantiateClass(editorClass);
|
||||
// Misbehaving ClassLoader returned null instead of ClassNotFoundException
|
||||
// - fall back to unknown editor type registration below
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("No property editor [" + editorName + "] found for type " +
|
||||
targetTypeName + " according to 'Editor' suffix convention");
|
||||
}
|
||||
unknownEditorTypes.add(targetType);
|
||||
return null;
|
||||
// Ignore - fall back to unknown editor type registration below
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("No property editor [" + editorName + "] found for type " +
|
||||
targetTypeName + " according to 'Editor' suffix convention");
|
||||
}
|
||||
unknownEditorTypes.add(targetType);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+24
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,8 @@ import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.net.URL;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -281,9 +283,17 @@ public final class CachedIntrospectionResults {
|
||||
// This call is slow so we do it once.
|
||||
PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors();
|
||||
for (PropertyDescriptor pd : pds) {
|
||||
if (Class.class == beanClass &&
|
||||
("classLoader".equals(pd.getName()) || "protectionDomain".equals(pd.getName()))) {
|
||||
// Ignore Class.getClassLoader() and getProtectionDomain() methods - nobody needs to bind to those
|
||||
if (Class.class == beanClass && !("name".equals(pd.getName()) ||
|
||||
(pd.getName().endsWith("Name") && String.class == pd.getPropertyType()))) {
|
||||
// Only allow all name variants of Class properties
|
||||
continue;
|
||||
}
|
||||
if (URL.class == beanClass && "content".equals(pd.getName())) {
|
||||
// Only allow URL attribute introspection, not content resolution
|
||||
continue;
|
||||
}
|
||||
if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) {
|
||||
// Ignore read-only properties such as ClassLoader - no need to bind to those
|
||||
continue;
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
@@ -321,6 +331,10 @@ public final class CachedIntrospectionResults {
|
||||
// GenericTypeAwarePropertyDescriptor leniently resolves a set* write method
|
||||
// against a declared read method, so we prefer read method descriptors here.
|
||||
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
|
||||
if (pd.getWriteMethod() == null && isInvalidReadOnlyPropertyType(pd.getPropertyType())) {
|
||||
// Ignore read-only properties such as ClassLoader - no need to bind to those
|
||||
continue;
|
||||
}
|
||||
this.propertyDescriptors.put(pd.getName(), pd);
|
||||
}
|
||||
}
|
||||
@@ -329,6 +343,12 @@ public final class CachedIntrospectionResults {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInvalidReadOnlyPropertyType(@Nullable Class<?> returnType) {
|
||||
return (returnType != null && (AutoCloseable.class.isAssignableFrom(returnType) ||
|
||||
ClassLoader.class.isAssignableFrom(returnType) ||
|
||||
ProtectionDomain.class.isAssignableFrom(returnType)));
|
||||
}
|
||||
|
||||
|
||||
BeanInfo getBeanInfo() {
|
||||
return this.beanInfo;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -92,8 +92,7 @@ public class DirectFieldAccessor extends AbstractNestablePropertyAccessor {
|
||||
@Override
|
||||
protected NotWritablePropertyException createNotWritablePropertyException(String propertyName) {
|
||||
PropertyMatches matches = PropertyMatches.forField(propertyName, getRootClass());
|
||||
throw new NotWritablePropertyException(
|
||||
getRootClass(), getNestedPath() + propertyName,
|
||||
throw new NotWritablePropertyException(getRootClass(), getNestedPath() + propertyName,
|
||||
matches.buildErrorMessage(), matches.getPossibleMatches());
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -138,7 +138,7 @@ final class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor {
|
||||
Set<Method> ambiguousCandidates = this.ambiguousWriteMethods;
|
||||
if (ambiguousCandidates != null) {
|
||||
this.ambiguousWriteMethods = null;
|
||||
LogFactory.getLog(GenericTypeAwarePropertyDescriptor.class).warn("Invalid JavaBean property '" +
|
||||
LogFactory.getLog(GenericTypeAwarePropertyDescriptor.class).debug("Non-unique JavaBean property '" +
|
||||
getName() + "' being accessed! Ambiguous write methods found next to actually used [" +
|
||||
this.writeMethod + "]: " + ambiguousCandidates);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,8 +23,9 @@ import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Common interface for classes that can access named properties
|
||||
* (such as bean properties of an object or fields in an object)
|
||||
* Serves as base interface for {@link BeanWrapper}.
|
||||
* (such as bean properties of an object or fields in an object).
|
||||
*
|
||||
* <p>Serves as base interface for {@link BeanWrapper}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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,10 +41,10 @@ public class TypeMismatchException extends PropertyAccessException {
|
||||
private String propertyName;
|
||||
|
||||
@Nullable
|
||||
private transient Object value;
|
||||
private final transient Object value;
|
||||
|
||||
@Nullable
|
||||
private Class<?> requiredType;
|
||||
private final Class<?> requiredType;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
+109
-86
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -459,7 +459,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
|
||||
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
|
||||
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
|
||||
return InjectionMetadata.EMPTY;
|
||||
}
|
||||
@@ -613,7 +613,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
|
||||
private final boolean required;
|
||||
|
||||
private volatile boolean cached = false;
|
||||
private volatile boolean cached;
|
||||
|
||||
@Nullable
|
||||
private volatile Object cachedFieldValue;
|
||||
@@ -628,46 +628,58 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
Field field = (Field) this.member;
|
||||
Object value;
|
||||
if (this.cached) {
|
||||
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
|
||||
try {
|
||||
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Unexpected removal of target bean for cached argument -> re-resolve
|
||||
value = resolveFieldValue(field, bean, beanName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
|
||||
desc.setContainingClass(bean.getClass());
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
|
||||
Assert.state(beanFactory != null, "No BeanFactory available");
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
try {
|
||||
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
if (value != null || this.required) {
|
||||
this.cachedFieldValue = desc;
|
||||
registerDependentBeans(beanName, autowiredBeanNames);
|
||||
if (autowiredBeanNames.size() == 1) {
|
||||
String autowiredBeanName = autowiredBeanNames.iterator().next();
|
||||
if (beanFactory.containsBean(autowiredBeanName) &&
|
||||
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
|
||||
this.cachedFieldValue = new ShortcutDependencyDescriptor(
|
||||
desc, autowiredBeanName, field.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.cachedFieldValue = null;
|
||||
}
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
value = resolveFieldValue(field, bean, beanName);
|
||||
}
|
||||
if (value != null) {
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
field.set(bean, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
|
||||
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
|
||||
desc.setContainingClass(bean.getClass());
|
||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
|
||||
Assert.state(beanFactory != null, "No BeanFactory available");
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
Object value;
|
||||
try {
|
||||
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
Object cachedFieldValue = null;
|
||||
if (value != null || this.required) {
|
||||
cachedFieldValue = desc;
|
||||
registerDependentBeans(beanName, autowiredBeanNames);
|
||||
if (autowiredBeanNames.size() == 1) {
|
||||
String autowiredBeanName = autowiredBeanNames.iterator().next();
|
||||
if (beanFactory.containsBean(autowiredBeanName) &&
|
||||
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
|
||||
cachedFieldValue = new ShortcutDependencyDescriptor(
|
||||
desc, autowiredBeanName, field.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
this.cachedFieldValue = cachedFieldValue;
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -678,7 +690,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
|
||||
private final boolean required;
|
||||
|
||||
private volatile boolean cached = false;
|
||||
private volatile boolean cached;
|
||||
|
||||
@Nullable
|
||||
private volatile Object[] cachedMethodArguments;
|
||||
@@ -696,58 +708,16 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
Method method = (Method) this.member;
|
||||
Object[] arguments;
|
||||
if (this.cached) {
|
||||
// Shortcut for avoiding synchronization...
|
||||
arguments = resolveCachedArguments(beanName);
|
||||
try {
|
||||
arguments = resolveCachedArguments(beanName);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Unexpected removal of target bean for cached argument -> re-resolve
|
||||
arguments = resolveMethodArguments(method, bean, beanName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
int argumentCount = method.getParameterCount();
|
||||
arguments = new Object[argumentCount];
|
||||
DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
|
||||
Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
|
||||
Assert.state(beanFactory != null, "No BeanFactory available");
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
MethodParameter methodParam = new MethodParameter(method, i);
|
||||
DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
|
||||
currDesc.setContainingClass(bean.getClass());
|
||||
descriptors[i] = currDesc;
|
||||
try {
|
||||
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
|
||||
if (arg == null && !this.required) {
|
||||
arguments = null;
|
||||
break;
|
||||
}
|
||||
arguments[i] = arg;
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
|
||||
}
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
if (arguments != null) {
|
||||
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
|
||||
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) &&
|
||||
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
|
||||
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
|
||||
descriptors[i], autowiredBeanName, paramTypes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.cachedMethodArguments = cachedMethodArguments;
|
||||
}
|
||||
else {
|
||||
this.cachedMethodArguments = null;
|
||||
}
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
arguments = resolveMethodArguments(method, bean, beanName);
|
||||
}
|
||||
if (arguments != null) {
|
||||
try {
|
||||
@@ -772,6 +742,59 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object[] resolveMethodArguments(Method method, Object bean, @Nullable String beanName) {
|
||||
int argumentCount = method.getParameterCount();
|
||||
Object[] arguments = new Object[argumentCount];
|
||||
DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
|
||||
Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
|
||||
Assert.state(beanFactory != null, "No BeanFactory available");
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
MethodParameter methodParam = new MethodParameter(method, i);
|
||||
DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
|
||||
currDesc.setContainingClass(bean.getClass());
|
||||
descriptors[i] = currDesc;
|
||||
try {
|
||||
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
|
||||
if (arg == null && !this.required) {
|
||||
arguments = null;
|
||||
break;
|
||||
}
|
||||
arguments[i] = arg;
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
|
||||
}
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
if (arguments != null) {
|
||||
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
|
||||
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) &&
|
||||
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
|
||||
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
|
||||
descriptors[i], autowiredBeanName, paramTypes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.cachedMethodArguments = cachedMethodArguments;
|
||||
}
|
||||
else {
|
||||
this.cachedMethodArguments = null;
|
||||
}
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-14
@@ -26,9 +26,6 @@ import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
@@ -69,8 +66,6 @@ public class InjectionMetadata {
|
||||
};
|
||||
|
||||
|
||||
private static final Log logger = LogFactory.getLog(InjectionMetadata.class);
|
||||
|
||||
private final Class<?> targetClass;
|
||||
|
||||
private final Collection<InjectedElement> injectedElements;
|
||||
@@ -110,9 +105,6 @@ public class InjectionMetadata {
|
||||
if (!beanDefinition.isExternallyManagedConfigMember(member)) {
|
||||
beanDefinition.registerExternallyManagedConfigMember(member);
|
||||
checkedElements.add(element);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Registered injected element on class [" + this.targetClass.getName() + "]: " + element);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.checkedElements = checkedElements;
|
||||
@@ -124,9 +116,6 @@ public class InjectionMetadata {
|
||||
(checkedElements != null ? checkedElements : this.injectedElements);
|
||||
if (!elementsToIterate.isEmpty()) {
|
||||
for (InjectedElement element : elementsToIterate) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Processing injected element of bean '" + beanName + "': " + element);
|
||||
}
|
||||
element.inject(target, beanName, pvs);
|
||||
}
|
||||
}
|
||||
@@ -152,12 +141,12 @@ public class InjectionMetadata {
|
||||
* Return an {@code InjectionMetadata} instance, possibly for empty elements.
|
||||
* @param elements the elements to inject (possibly empty)
|
||||
* @param clazz the target class
|
||||
* @return a new {@link #InjectionMetadata(Class, Collection)} instance,
|
||||
* or {@link #EMPTY} in case of no elements
|
||||
* @return a new {@link #InjectionMetadata(Class, Collection)} instance
|
||||
* @since 5.2
|
||||
*/
|
||||
public static InjectionMetadata forElements(Collection<InjectedElement> elements, Class<?> clazz) {
|
||||
return (elements.isEmpty() ? InjectionMetadata.EMPTY : new InjectionMetadata(clazz, elements));
|
||||
return (elements.isEmpty() ? new InjectionMetadata(clazz, Collections.emptyList()) :
|
||||
new InjectionMetadata(clazz, elements));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -351,7 +351,9 @@ public class ConstructorArgumentValues {
|
||||
* @return the ValueHolder for the argument, or {@code null} if none set
|
||||
*/
|
||||
@Nullable
|
||||
public ValueHolder getArgumentValue(int index, @Nullable Class<?> requiredType, @Nullable String requiredName, @Nullable Set<ValueHolder> usedValueHolders) {
|
||||
public ValueHolder getArgumentValue(int index, @Nullable Class<?> requiredType,
|
||||
@Nullable String requiredName, @Nullable Set<ValueHolder> usedValueHolders) {
|
||||
|
||||
Assert.isTrue(index >= 0, "Index must not be negative");
|
||||
ValueHolder valueHolder = getIndexedArgumentValue(index, requiredType, requiredName);
|
||||
if (valueHolder == null) {
|
||||
|
||||
+11
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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,15 +36,16 @@ import org.springframework.util.StringValueResolver;
|
||||
* Example XML bean definition:
|
||||
*
|
||||
* <pre class="code">
|
||||
* <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"/>
|
||||
* <property name="driverClassName" value="${driver}"/>
|
||||
* <property name="url" value="jdbc:${dbname}"/>
|
||||
* <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
|
||||
* <property name="driverClassName" value="${driver}" />
|
||||
* <property name="url" value="jdbc:${dbname}" />
|
||||
* </bean>
|
||||
* </pre>
|
||||
*
|
||||
* Example properties file:
|
||||
*
|
||||
* <pre class="code">driver=com.mysql.jdbc.Driver
|
||||
* <pre class="code">
|
||||
* driver=com.mysql.jdbc.Driver
|
||||
* dbname=mysql:mydb</pre>
|
||||
*
|
||||
* Annotated bean definitions may take advantage of property replacement using
|
||||
@@ -56,7 +57,8 @@ import org.springframework.util.StringValueResolver;
|
||||
* in bean references. Furthermore, placeholder values can also cross-reference
|
||||
* other placeholders, like:
|
||||
*
|
||||
* <pre class="code">rootPath=myrootdir
|
||||
* <pre class="code">
|
||||
* rootPath=myrootdir
|
||||
* subPath=${rootPath}/subdir</pre>
|
||||
*
|
||||
* In contrast to {@link PropertyOverrideConfigurer}, subclasses of this type allow
|
||||
@@ -71,13 +73,13 @@ import org.springframework.util.StringValueResolver;
|
||||
*
|
||||
* <p>Default property values can be defined globally for each configurer instance
|
||||
* via the {@link #setProperties properties} property, or on a property-by-property basis
|
||||
* using the default value separator which is {@code ":"} by default and
|
||||
* customizable via {@link #setValueSeparator(String)}.
|
||||
* using the value separator which is {@code ":"} by default and customizable via
|
||||
* {@link #setValueSeparator(String)}.
|
||||
*
|
||||
* <p>Example XML property with default value:
|
||||
*
|
||||
* <pre class="code">
|
||||
* <property name="url" value="jdbc:${dbname:defaultdb}"/>
|
||||
* <property name="url" value="jdbc:${dbname:defaultdb}" />
|
||||
* </pre>
|
||||
*
|
||||
* @author Chris Beams
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -489,8 +489,8 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
|
||||
resolveConstructorArguments(args, 2, hasClosureArgument ? args.length - 1 : args.length);
|
||||
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, (Class<?>) args[1], constructorArgs);
|
||||
Map<?, ?> namedArgs = (Map<?, ?>) args[0];
|
||||
for (Object o : namedArgs.keySet()) {
|
||||
String propName = (String) o;
|
||||
for (Object key : namedArgs.keySet()) {
|
||||
String propName = (String) key;
|
||||
setProperty(propName, namedArgs.get(propName));
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -1013,6 +1013,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
throw ex;
|
||||
}
|
||||
catch (BeanCreationException ex) {
|
||||
// Don't swallow a linkage error since it contains a full stacktrace on
|
||||
// first occurrence... and just a plain NoClassDefFoundError afterwards.
|
||||
if (ex.contains(LinkageError.class)) {
|
||||
throw ex;
|
||||
}
|
||||
// Instantiation failure, maybe too early...
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Bean creation exception on singleton FactoryBean type check: " + ex);
|
||||
|
||||
+23
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -1194,8 +1194,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
||||
this.primary == that.primary &&
|
||||
this.nonPublicAccessAllowed == that.nonPublicAccessAllowed &&
|
||||
this.lenientConstructorResolution == that.lenientConstructorResolution &&
|
||||
ObjectUtils.nullSafeEquals(this.constructorArgumentValues, that.constructorArgumentValues) &&
|
||||
ObjectUtils.nullSafeEquals(this.propertyValues, that.propertyValues) &&
|
||||
equalsConstructorArgumentValues(that) &&
|
||||
equalsPropertyValues(that) &&
|
||||
ObjectUtils.nullSafeEquals(this.methodOverrides, that.methodOverrides) &&
|
||||
ObjectUtils.nullSafeEquals(this.factoryBeanName, that.factoryBeanName) &&
|
||||
ObjectUtils.nullSafeEquals(this.factoryMethodName, that.factoryMethodName) &&
|
||||
@@ -1208,12 +1208,30 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
||||
super.equals(other));
|
||||
}
|
||||
|
||||
private boolean equalsConstructorArgumentValues(AbstractBeanDefinition other) {
|
||||
if (!hasConstructorArgumentValues()) {
|
||||
return !other.hasConstructorArgumentValues();
|
||||
}
|
||||
return ObjectUtils.nullSafeEquals(this.constructorArgumentValues, other.constructorArgumentValues);
|
||||
}
|
||||
|
||||
private boolean equalsPropertyValues(AbstractBeanDefinition other) {
|
||||
if (!hasPropertyValues()) {
|
||||
return !other.hasPropertyValues();
|
||||
}
|
||||
return ObjectUtils.nullSafeEquals(this.propertyValues, other.propertyValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = ObjectUtils.nullSafeHashCode(getBeanClassName());
|
||||
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.scope);
|
||||
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.constructorArgumentValues);
|
||||
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.propertyValues);
|
||||
if (hasConstructorArgumentValues()) {
|
||||
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.constructorArgumentValues);
|
||||
}
|
||||
if (hasPropertyValues()) {
|
||||
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.propertyValues);
|
||||
}
|
||||
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.factoryBeanName);
|
||||
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.factoryMethodName);
|
||||
hashCode = 29 * hashCode + super.hashCode();
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -350,7 +350,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
else {
|
||||
String scopeName = mbd.getScope();
|
||||
if (!StringUtils.hasLength(scopeName)) {
|
||||
throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
|
||||
throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
|
||||
}
|
||||
Scope scope = this.scopes.get(scopeName);
|
||||
if (scope == null) {
|
||||
|
||||
+14
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -66,6 +66,7 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Delegate for resolving constructors and factory methods.
|
||||
*
|
||||
* <p>Performs constructor resolution through argument matching.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
@@ -84,7 +85,7 @@ class ConstructorResolver {
|
||||
private static final Object[] EMPTY_ARGS = new Object[0];
|
||||
|
||||
/**
|
||||
* Marker for autowired arguments in a cached argument array, to be later replaced
|
||||
* Marker for autowired arguments in a cached argument array, to be replaced
|
||||
* by a {@linkplain #resolveAutowiredArgument resolved autowired argument}.
|
||||
*/
|
||||
private static final Object autowiredArgumentMarker = new Object();
|
||||
@@ -148,7 +149,7 @@ class ConstructorResolver {
|
||||
}
|
||||
}
|
||||
if (argsToResolve != null) {
|
||||
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
|
||||
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,12 +276,12 @@ class ConstructorResolver {
|
||||
throw ex;
|
||||
}
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
"Could not resolve matching constructor " +
|
||||
"Could not resolve matching constructor on bean class [" + mbd.getBeanClassName() + "] " +
|
||||
"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");
|
||||
}
|
||||
else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
"Ambiguous constructor matches found in bean '" + beanName + "' " +
|
||||
"Ambiguous constructor matches found on bean class [" + mbd.getBeanClassName() + "] " +
|
||||
"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
|
||||
ambiguousConstructors);
|
||||
}
|
||||
@@ -409,6 +410,7 @@ class ConstructorResolver {
|
||||
if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
|
||||
throw new ImplicitlyAppearedSingletonException();
|
||||
}
|
||||
this.beanFactory.registerDependentBean(factoryBeanName, beanName);
|
||||
factoryClass = factoryBean.getClass();
|
||||
isStatic = false;
|
||||
}
|
||||
@@ -443,7 +445,7 @@ class ConstructorResolver {
|
||||
}
|
||||
}
|
||||
if (argsToResolve != null) {
|
||||
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);
|
||||
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,7 +607,7 @@ class ConstructorResolver {
|
||||
}
|
||||
String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
"No matching factory method found: " +
|
||||
"No matching factory method found on class [" + factoryClass.getName() + "]: " +
|
||||
(mbd.getFactoryBeanName() != null ?
|
||||
"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
|
||||
"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +
|
||||
@@ -616,12 +618,12 @@ class ConstructorResolver {
|
||||
}
|
||||
else if (void.class == factoryMethodToUse.getReturnType()) {
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
"Invalid factory method '" + mbd.getFactoryMethodName() +
|
||||
"': needs to have a non-void return type!");
|
||||
"Invalid factory method '" + mbd.getFactoryMethodName() + "' on class [" +
|
||||
factoryClass.getName() + "]: needs to have a non-void return type!");
|
||||
}
|
||||
else if (ambiguousFactoryMethods != null) {
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
"Ambiguous factory method matches found in bean '" + beanName + "' " +
|
||||
"Ambiguous factory method matches found on class [" + factoryClass.getName() + "] " +
|
||||
"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
|
||||
ambiguousFactoryMethods);
|
||||
}
|
||||
@@ -815,7 +817,7 @@ class ConstructorResolver {
|
||||
* Resolve the prepared arguments stored in the given bean definition.
|
||||
*/
|
||||
private Object[] resolvePreparedArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
|
||||
Executable executable, Object[] argsToResolve, boolean fallback) {
|
||||
Executable executable, Object[] argsToResolve) {
|
||||
|
||||
TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
|
||||
TypeConverter converter = (customConverter != null ? customConverter : bw);
|
||||
@@ -828,7 +830,7 @@ class ConstructorResolver {
|
||||
Object argValue = argsToResolve[argIndex];
|
||||
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
|
||||
if (argValue == autowiredArgumentMarker) {
|
||||
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, fallback);
|
||||
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, true);
|
||||
}
|
||||
else if (argValue instanceof BeanMetadataElement) {
|
||||
argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
|
||||
|
||||
+86
-79
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -62,6 +62,8 @@ import org.springframework.util.StringUtils;
|
||||
@SuppressWarnings("serial")
|
||||
class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
|
||||
private static final String DESTROY_METHOD_NAME = "destroy";
|
||||
|
||||
private static final String CLOSE_METHOD_NAME = "close";
|
||||
|
||||
private static final String SHUTDOWN_METHOD_NAME = "shutdown";
|
||||
@@ -87,7 +89,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
private transient Method destroyMethod;
|
||||
|
||||
@Nullable
|
||||
private List<DestructionAwareBeanPostProcessor> beanPostProcessors;
|
||||
private final List<DestructionAwareBeanPostProcessor> beanPostProcessors;
|
||||
|
||||
|
||||
/**
|
||||
@@ -104,13 +106,16 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
Assert.notNull(bean, "Disposable bean must not be null");
|
||||
this.bean = bean;
|
||||
this.beanName = beanName;
|
||||
this.invokeDisposableBean =
|
||||
(this.bean instanceof DisposableBean && !beanDefinition.isExternallyManagedDestroyMethod("destroy"));
|
||||
this.invokeDisposableBean = (bean instanceof DisposableBean &&
|
||||
!beanDefinition.isExternallyManagedDestroyMethod(DESTROY_METHOD_NAME));
|
||||
this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed();
|
||||
this.acc = acc;
|
||||
|
||||
String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
|
||||
if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) &&
|
||||
if (destroyMethodName != null &&
|
||||
!(this.invokeDisposableBean && DESTROY_METHOD_NAME.equals(destroyMethodName)) &&
|
||||
!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
|
||||
|
||||
this.destroyMethodName = destroyMethodName;
|
||||
Method destroyMethod = determineDestroyMethod(destroyMethodName);
|
||||
if (destroyMethod == null) {
|
||||
@@ -120,19 +125,22 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
}
|
||||
}
|
||||
else {
|
||||
Class<?>[] paramTypes = destroyMethod.getParameterTypes();
|
||||
if (paramTypes.length > 1) {
|
||||
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
|
||||
beanName + "' has more than one parameter - not supported as destroy method");
|
||||
}
|
||||
else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) {
|
||||
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
|
||||
beanName + "' has a non-boolean parameter - not supported as destroy method");
|
||||
if (destroyMethod.getParameterCount() > 0) {
|
||||
Class<?>[] paramTypes = destroyMethod.getParameterTypes();
|
||||
if (paramTypes.length > 1) {
|
||||
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
|
||||
beanName + "' has more than one parameter - not supported as destroy method");
|
||||
}
|
||||
else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) {
|
||||
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
|
||||
beanName + "' has a non-boolean parameter - not supported as destroy method");
|
||||
}
|
||||
}
|
||||
destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod);
|
||||
}
|
||||
this.destroyMethod = destroyMethod;
|
||||
}
|
||||
|
||||
this.beanPostProcessors = filterPostProcessors(postProcessors, bean);
|
||||
}
|
||||
|
||||
@@ -169,67 +177,6 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the current value of the given beanDefinition's "destroyMethodName" property is
|
||||
* {@link AbstractBeanDefinition#INFER_METHOD}, then attempt to infer a destroy method.
|
||||
* Candidate methods are currently limited to public, no-arg methods named "close" or
|
||||
* "shutdown" (whether declared locally or inherited). The given BeanDefinition's
|
||||
* "destroyMethodName" is updated to be null if no such method is found, otherwise set
|
||||
* to the name of the inferred method. This constant serves as the default for the
|
||||
* {@code @Bean#destroyMethod} attribute and the value of the constant may also be
|
||||
* used in XML within the {@code <bean destroy-method="">} or {@code
|
||||
* <beans default-destroy-method="">} attributes.
|
||||
* <p>Also processes the {@link java.io.Closeable} and {@link java.lang.AutoCloseable}
|
||||
* interfaces, reflectively calling the "close" method on implementing beans as well.
|
||||
*/
|
||||
@Nullable
|
||||
private String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
|
||||
String destroyMethodName = beanDefinition.getDestroyMethodName();
|
||||
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
|
||||
(destroyMethodName == null && bean instanceof AutoCloseable)) {
|
||||
// Only perform destroy method inference or Closeable detection
|
||||
// in case of the bean not explicitly implementing DisposableBean
|
||||
if (!(bean instanceof DisposableBean)) {
|
||||
try {
|
||||
return bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
try {
|
||||
return bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
|
||||
}
|
||||
catch (NoSuchMethodException ex2) {
|
||||
// no candidate destroy method found
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for all DestructionAwareBeanPostProcessors in the List.
|
||||
* @param processors the List to search
|
||||
* @return the filtered List of DestructionAwareBeanPostProcessors
|
||||
*/
|
||||
@Nullable
|
||||
private List<DestructionAwareBeanPostProcessor> filterPostProcessors(List<BeanPostProcessor> processors, Object bean) {
|
||||
List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null;
|
||||
if (!CollectionUtils.isEmpty(processors)) {
|
||||
filteredPostProcessors = new ArrayList<>(processors.size());
|
||||
for (BeanPostProcessor processor : processors) {
|
||||
if (processor instanceof DestructionAwareBeanPostProcessor) {
|
||||
DestructionAwareBeanPostProcessor dabpp = (DestructionAwareBeanPostProcessor) processor;
|
||||
if (dabpp.requiresDestruction(bean)) {
|
||||
filteredPostProcessors.add(dabpp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredPostProcessors;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
destroy();
|
||||
@@ -384,12 +331,50 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
if (bean instanceof DisposableBean || bean instanceof AutoCloseable) {
|
||||
return true;
|
||||
}
|
||||
String destroyMethodName = beanDefinition.getDestroyMethodName();
|
||||
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName)) {
|
||||
return (ClassUtils.hasMethod(bean.getClass(), CLOSE_METHOD_NAME) ||
|
||||
ClassUtils.hasMethod(bean.getClass(), SHUTDOWN_METHOD_NAME));
|
||||
return inferDestroyMethodIfNecessary(bean, beanDefinition) != null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the current value of the given beanDefinition's "destroyMethodName" property is
|
||||
* {@link AbstractBeanDefinition#INFER_METHOD}, then attempt to infer a destroy method.
|
||||
* Candidate methods are currently limited to public, no-arg methods named "close" or
|
||||
* "shutdown" (whether declared locally or inherited). The given BeanDefinition's
|
||||
* "destroyMethodName" is updated to be null if no such method is found, otherwise set
|
||||
* to the name of the inferred method. This constant serves as the default for the
|
||||
* {@code @Bean#destroyMethod} attribute and the value of the constant may also be
|
||||
* used in XML within the {@code <bean destroy-method="">} or {@code
|
||||
* <beans default-destroy-method="">} attributes.
|
||||
* <p>Also processes the {@link java.io.Closeable} and {@link java.lang.AutoCloseable}
|
||||
* interfaces, reflectively calling the "close" method on implementing beans as well.
|
||||
*/
|
||||
@Nullable
|
||||
private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
|
||||
String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
|
||||
if (destroyMethodName == null) {
|
||||
destroyMethodName = beanDefinition.getDestroyMethodName();
|
||||
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
|
||||
(destroyMethodName == null && bean instanceof AutoCloseable)) {
|
||||
// Only perform destroy method inference in case of the bean
|
||||
// not explicitly implementing the DisposableBean interface
|
||||
destroyMethodName = null;
|
||||
if (!(bean instanceof DisposableBean)) {
|
||||
try {
|
||||
destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
try {
|
||||
destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
|
||||
}
|
||||
catch (NoSuchMethodException ex2) {
|
||||
// no candidate destroy method found
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");
|
||||
}
|
||||
return StringUtils.hasLength(destroyMethodName);
|
||||
return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -411,4 +396,26 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for all DestructionAwareBeanPostProcessors in the List.
|
||||
* @param processors the List to search
|
||||
* @return the filtered List of DestructionAwareBeanPostProcessors
|
||||
*/
|
||||
@Nullable
|
||||
private List<DestructionAwareBeanPostProcessor> filterPostProcessors(List<BeanPostProcessor> processors, Object bean) {
|
||||
List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null;
|
||||
if (!CollectionUtils.isEmpty(processors)) {
|
||||
filteredPostProcessors = new ArrayList<>(processors.size());
|
||||
for (BeanPostProcessor processor : processors) {
|
||||
if (processor instanceof DestructionAwareBeanPostProcessor) {
|
||||
DestructionAwareBeanPostProcessor dabpp = (DestructionAwareBeanPostProcessor) processor;
|
||||
if (dabpp.requiresDestruction(bean)) {
|
||||
filteredPostProcessors.add(dabpp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredPostProcessors;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -65,7 +65,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
|
||||
boolean allowCaching = true;
|
||||
|
||||
boolean isFactoryMethodUnique = false;
|
||||
boolean isFactoryMethodUnique;
|
||||
|
||||
@Nullable
|
||||
volatile ResolvableType targetType;
|
||||
@@ -86,6 +86,10 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||
@Nullable
|
||||
volatile Method factoryMethodToIntrospect;
|
||||
|
||||
/** Package-visible field for caching a resolved destroy method name (also for inferred). */
|
||||
@Nullable
|
||||
volatile String resolvedDestroyMethodName;
|
||||
|
||||
/** Common lock for the four constructor fields below. */
|
||||
final Object constructorArgumentLock = new Object();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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,24 +64,24 @@ public final class ParserContext {
|
||||
}
|
||||
|
||||
|
||||
public final XmlReaderContext getReaderContext() {
|
||||
public XmlReaderContext getReaderContext() {
|
||||
return this.readerContext;
|
||||
}
|
||||
|
||||
public final BeanDefinitionRegistry getRegistry() {
|
||||
public BeanDefinitionRegistry getRegistry() {
|
||||
return this.readerContext.getRegistry();
|
||||
}
|
||||
|
||||
public final BeanDefinitionParserDelegate getDelegate() {
|
||||
public BeanDefinitionParserDelegate getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public final BeanDefinition getContainingBeanDefinition() {
|
||||
public BeanDefinition getContainingBeanDefinition() {
|
||||
return this.containingBeanDefinition;
|
||||
}
|
||||
|
||||
public final boolean isNested() {
|
||||
public boolean isNested() {
|
||||
return (this.containingBeanDefinition != null);
|
||||
}
|
||||
|
||||
|
||||
+11
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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 java.nio.file.Paths;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceEditor;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
/**
|
||||
* Editor for {@code java.nio.file.Path}, to directly populate a Path
|
||||
@@ -74,7 +74,7 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
|
||||
@Override
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
boolean nioPathCandidate = !text.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX);
|
||||
boolean nioPathCandidate = !text.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX);
|
||||
if (nioPathCandidate && !text.startsWith("/")) {
|
||||
try {
|
||||
URI uri = new URI(text);
|
||||
@@ -85,9 +85,13 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (URISyntaxException | FileSystemNotFoundException ex) {
|
||||
// Not a valid URI (let's try as Spring resource location),
|
||||
// or a URI scheme not registered for NIO (let's try URL
|
||||
catch (URISyntaxException ex) {
|
||||
// Not a valid URI; potentially a Windows-style path after
|
||||
// a file prefix (let's try as Spring resource location)
|
||||
nioPathCandidate = !text.startsWith(ResourceUtils.FILE_URL_PREFIX);
|
||||
}
|
||||
catch (FileSystemNotFoundException ex) {
|
||||
// URI scheme not registered for NIO (let's try URL
|
||||
// protocol handlers via Spring's resource mechanism).
|
||||
}
|
||||
}
|
||||
@@ -97,7 +101,7 @@ public class PathEditor extends PropertyEditorSupport {
|
||||
if (resource == null) {
|
||||
setValue(null);
|
||||
}
|
||||
else if (!resource.exists() && nioPathCandidate) {
|
||||
else if (nioPathCandidate && !resource.exists()) {
|
||||
setValue(Paths.get(text).normalize());
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.springframework.lang.Nullable;
|
||||
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.assertj.core.api.SoftAssertions.assertSoftly;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link BeanUtils}.
|
||||
@@ -80,19 +81,43 @@ class BeanUtilsTests {
|
||||
}
|
||||
|
||||
@Test // gh-22531
|
||||
void testInstantiateClassWithOptionalPrimitiveType() throws NoSuchMethodException {
|
||||
Constructor<BeanWithPrimitiveTypes> ctor = BeanWithPrimitiveTypes.class.getDeclaredConstructor(int.class, boolean.class, String.class);
|
||||
BeanWithPrimitiveTypes bean = BeanUtils.instantiateClass(ctor, null, null, "foo");
|
||||
assertThat(bean.getCounter()).isEqualTo(0);
|
||||
assertThat(bean.isFlag()).isEqualTo(false);
|
||||
assertThat(bean.getValue()).isEqualTo("foo");
|
||||
void instantiateClassWithFewerArgsThanParameters() throws NoSuchMethodException {
|
||||
Constructor<BeanWithPrimitiveTypes> constructor = getBeanWithPrimitiveTypesConstructor();
|
||||
|
||||
assertThatExceptionOfType(BeanInstantiationException.class).isThrownBy(() ->
|
||||
BeanUtils.instantiateClass(constructor, null, null, "foo"));
|
||||
}
|
||||
|
||||
@Test // gh-22531
|
||||
void testInstantiateClassWithMoreArgsThanParameters() throws NoSuchMethodException {
|
||||
Constructor<BeanWithPrimitiveTypes> ctor = BeanWithPrimitiveTypes.class.getDeclaredConstructor(int.class, boolean.class, String.class);
|
||||
@Test // gh-22531
|
||||
void instantiateClassWithMoreArgsThanParameters() throws NoSuchMethodException {
|
||||
Constructor<BeanWithPrimitiveTypes> constructor = getBeanWithPrimitiveTypesConstructor();
|
||||
|
||||
assertThatExceptionOfType(BeanInstantiationException.class).isThrownBy(() ->
|
||||
BeanUtils.instantiateClass(ctor, null, null, "foo", null));
|
||||
BeanUtils.instantiateClass(constructor, null, null, null, null, null, null, null, null, "foo", null));
|
||||
}
|
||||
|
||||
@Test // gh-22531, gh-27390
|
||||
void instantiateClassWithOptionalPrimitiveTypes() throws NoSuchMethodException {
|
||||
Constructor<BeanWithPrimitiveTypes> constructor = getBeanWithPrimitiveTypesConstructor();
|
||||
|
||||
BeanWithPrimitiveTypes bean = BeanUtils.instantiateClass(constructor, null, null, null, null, null, null, null, null, "foo");
|
||||
|
||||
assertSoftly(softly -> {
|
||||
softly.assertThat(bean.isFlag()).isFalse();
|
||||
softly.assertThat(bean.getByteCount()).isEqualTo((byte) 0);
|
||||
softly.assertThat(bean.getShortCount()).isEqualTo((short) 0);
|
||||
softly.assertThat(bean.getIntCount()).isEqualTo(0);
|
||||
softly.assertThat(bean.getLongCount()).isEqualTo(0L);
|
||||
softly.assertThat(bean.getFloatCount()).isEqualTo(0F);
|
||||
softly.assertThat(bean.getDoubleCount()).isEqualTo(0D);
|
||||
softly.assertThat(bean.getCharacter()).isEqualTo('\0');
|
||||
softly.assertThat(bean.getText()).isEqualTo("foo");
|
||||
});
|
||||
}
|
||||
|
||||
private Constructor<BeanWithPrimitiveTypes> getBeanWithPrimitiveTypesConstructor() throws NoSuchMethodException {
|
||||
return BeanWithPrimitiveTypes.class.getConstructor(boolean.class, byte.class, short.class, int.class,
|
||||
long.class, float.class, double.class, char.class, String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -535,30 +560,68 @@ class BeanUtilsTests {
|
||||
|
||||
private static class BeanWithPrimitiveTypes {
|
||||
|
||||
private int counter;
|
||||
|
||||
private boolean flag;
|
||||
private byte byteCount;
|
||||
private short shortCount;
|
||||
private int intCount;
|
||||
private long longCount;
|
||||
private float floatCount;
|
||||
private double doubleCount;
|
||||
private char character;
|
||||
private String text;
|
||||
|
||||
private String value;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public BeanWithPrimitiveTypes(int counter, boolean flag, String value) {
|
||||
this.counter = counter;
|
||||
this.flag = flag;
|
||||
this.value = value;
|
||||
}
|
||||
public BeanWithPrimitiveTypes(boolean flag, byte byteCount, short shortCount, int intCount, long longCount,
|
||||
float floatCount, double doubleCount, char character, String text) {
|
||||
|
||||
public int getCounter() {
|
||||
return counter;
|
||||
this.flag = flag;
|
||||
this.byteCount = byteCount;
|
||||
this.shortCount = shortCount;
|
||||
this.intCount = intCount;
|
||||
this.longCount = longCount;
|
||||
this.floatCount = floatCount;
|
||||
this.doubleCount = doubleCount;
|
||||
this.character = character;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public boolean isFlag() {
|
||||
return flag;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
public byte getByteCount() {
|
||||
return byteCount;
|
||||
}
|
||||
|
||||
public short getShortCount() {
|
||||
return shortCount;
|
||||
}
|
||||
|
||||
public int getIntCount() {
|
||||
return intCount;
|
||||
}
|
||||
|
||||
public long getLongCount() {
|
||||
return longCount;
|
||||
}
|
||||
|
||||
public float getFloatCount() {
|
||||
return floatCount;
|
||||
}
|
||||
|
||||
public double getDoubleCount() {
|
||||
return doubleCount;
|
||||
}
|
||||
|
||||
public char getCharacter() {
|
||||
return character;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class PrivateBeanWithPrivateConstructor {
|
||||
|
||||
+47
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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 static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
/**
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class BeanWrapperAutoGrowingTests {
|
||||
|
||||
@@ -37,7 +38,7 @@ public class BeanWrapperAutoGrowingTests {
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
public void setup() {
|
||||
wrapper.setAutoGrowNestedPaths(true);
|
||||
}
|
||||
|
||||
@@ -66,11 +67,6 @@ public class BeanWrapperAutoGrowingTests {
|
||||
assertThat(bean.getArray()[0]).isInstanceOf(Bean.class);
|
||||
}
|
||||
|
||||
private void assertNotNull(Object propertyValue) {
|
||||
assertThat(propertyValue).isNotNull();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void setPropertyValueAutoGrowArray() {
|
||||
wrapper.setPropertyValue("array[0].prop", "test");
|
||||
@@ -93,12 +89,39 @@ public class BeanWrapperAutoGrowingTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPropertyValueAutoGrowMultiDimensionalArray() {
|
||||
public void getPropertyValueAutoGrow2dArray() {
|
||||
assertNotNull(wrapper.getPropertyValue("multiArray[0][0]"));
|
||||
assertThat(bean.getMultiArray()[0].length).isEqualTo(1);
|
||||
assertThat(bean.getMultiArray()[0][0]).isInstanceOf(Bean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPropertyValueAutoGrow3dArray() {
|
||||
assertNotNull(wrapper.getPropertyValue("threeDimensionalArray[1][2][3]"));
|
||||
assertThat(bean.getThreeDimensionalArray()[1].length).isEqualTo(3);
|
||||
assertThat(bean.getThreeDimensionalArray()[1][2][3]).isInstanceOf(Bean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setPropertyValueAutoGrow2dArray() {
|
||||
Bean newBean = new Bean();
|
||||
newBean.setProp("enigma");
|
||||
wrapper.setPropertyValue("multiArray[2][3]", newBean);
|
||||
assertThat(bean.getMultiArray()[2][3])
|
||||
.isInstanceOf(Bean.class)
|
||||
.extracting(Bean::getProp).isEqualTo("enigma");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setPropertyValueAutoGrow3dArray() {
|
||||
Bean newBean = new Bean();
|
||||
newBean.setProp("enigma");
|
||||
wrapper.setPropertyValue("threeDimensionalArray[2][3][4]", newBean);
|
||||
assertThat(bean.getThreeDimensionalArray()[2][3][4])
|
||||
.isInstanceOf(Bean.class)
|
||||
.extracting(Bean::getProp).isEqualTo("enigma");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPropertyValueAutoGrowList() {
|
||||
assertNotNull(wrapper.getPropertyValue("list[0]"));
|
||||
@@ -131,7 +154,7 @@ public class BeanWrapperAutoGrowingTests {
|
||||
public void getPropertyValueAutoGrowListFailsAgainstLimit() {
|
||||
wrapper.setAutoGrowCollectionLimit(2);
|
||||
assertThatExceptionOfType(InvalidPropertyException.class).isThrownBy(() ->
|
||||
assertNotNull(wrapper.getPropertyValue("list[4]")))
|
||||
wrapper.getPropertyValue("list[4]"))
|
||||
.withRootCauseInstanceOf(IndexOutOfBoundsException.class);
|
||||
}
|
||||
|
||||
@@ -161,6 +184,11 @@ public class BeanWrapperAutoGrowingTests {
|
||||
}
|
||||
|
||||
|
||||
private static void assertNotNull(Object propertyValue) {
|
||||
assertThat(propertyValue).isNotNull();
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static class Bean {
|
||||
|
||||
@@ -174,6 +202,8 @@ public class BeanWrapperAutoGrowingTests {
|
||||
|
||||
private Bean[][] multiArray;
|
||||
|
||||
private Bean[][][] threeDimensionalArray;
|
||||
|
||||
private List<Bean> list;
|
||||
|
||||
private List<List<Bean>> multiList;
|
||||
@@ -214,6 +244,14 @@ public class BeanWrapperAutoGrowingTests {
|
||||
this.multiArray = multiArray;
|
||||
}
|
||||
|
||||
public Bean[][][] getThreeDimensionalArray() {
|
||||
return threeDimensionalArray;
|
||||
}
|
||||
|
||||
public void setThreeDimensionalArray(Bean[][][] threeDimensionalArray) {
|
||||
this.threeDimensionalArray = threeDimensionalArray;
|
||||
}
|
||||
|
||||
public List<Bean> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
+28
-36
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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,7 +16,6 @@
|
||||
|
||||
package org.springframework.beans;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -86,7 +85,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericList() throws MalformedURLException {
|
||||
public void testGenericList() throws Exception {
|
||||
GenericBean<?> gb = new GenericBean<>();
|
||||
BeanWrapper bw = new BeanWrapperImpl(gb);
|
||||
List<String> input = new ArrayList<>();
|
||||
@@ -98,7 +97,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericListElement() throws MalformedURLException {
|
||||
public void testGenericListElement() throws Exception {
|
||||
GenericBean<?> gb = new GenericBean<>();
|
||||
gb.setResourceList(new ArrayList<>());
|
||||
BeanWrapper bw = new BeanWrapperImpl(gb);
|
||||
@@ -163,10 +162,8 @@ public class BeanWrapperGenericsTests {
|
||||
value2.add(Boolean.TRUE);
|
||||
input.put("2", value2);
|
||||
bw.setPropertyValue("collectionMap", input);
|
||||
boolean condition1 = gb.getCollectionMap().get(1) instanceof HashSet;
|
||||
assertThat(condition1).isTrue();
|
||||
boolean condition = gb.getCollectionMap().get(2) instanceof ArrayList;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(gb.getCollectionMap().get(1) instanceof HashSet).isTrue();
|
||||
assertThat(gb.getCollectionMap().get(2) instanceof ArrayList).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -178,8 +175,7 @@ public class BeanWrapperGenericsTests {
|
||||
HashSet<Integer> value1 = new HashSet<>();
|
||||
value1.add(1);
|
||||
bw.setPropertyValue("collectionMap[1]", value1);
|
||||
boolean condition = gb.getCollectionMap().get(1) instanceof HashSet;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(gb.getCollectionMap().get(1) instanceof HashSet).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -195,7 +191,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericListOfLists() throws MalformedURLException {
|
||||
public void testGenericListOfLists() {
|
||||
GenericBean<String> gb = new GenericBean<>();
|
||||
List<List<Integer>> list = new LinkedList<>();
|
||||
list.add(new LinkedList<>());
|
||||
@@ -207,7 +203,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericListOfListsWithElementConversion() throws MalformedURLException {
|
||||
public void testGenericListOfListsWithElementConversion() {
|
||||
GenericBean<String> gb = new GenericBean<>();
|
||||
List<List<Integer>> list = new LinkedList<>();
|
||||
list.add(new LinkedList<>());
|
||||
@@ -219,7 +215,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericListOfArrays() throws MalformedURLException {
|
||||
public void testGenericListOfArrays() {
|
||||
GenericBean<String> gb = new GenericBean<>();
|
||||
ArrayList<String[]> list = new ArrayList<>();
|
||||
list.add(new String[] {"str1", "str2"});
|
||||
@@ -231,7 +227,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericListOfArraysWithElementConversion() throws MalformedURLException {
|
||||
public void testGenericListOfArraysWithElementConversion() {
|
||||
GenericBean<String> gb = new GenericBean<>();
|
||||
ArrayList<String[]> list = new ArrayList<>();
|
||||
list.add(new String[] {"str1", "str2"});
|
||||
@@ -244,7 +240,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericListOfMaps() throws MalformedURLException {
|
||||
public void testGenericListOfMaps() {
|
||||
GenericBean<String> gb = new GenericBean<>();
|
||||
List<Map<Integer, Long>> list = new LinkedList<>();
|
||||
list.add(new HashMap<>());
|
||||
@@ -256,7 +252,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericListOfMapsWithElementConversion() throws MalformedURLException {
|
||||
public void testGenericListOfMapsWithElementConversion() {
|
||||
GenericBean<String> gb = new GenericBean<>();
|
||||
List<Map<Integer, Long>> list = new LinkedList<>();
|
||||
list.add(new HashMap<>());
|
||||
@@ -268,7 +264,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericMapOfMaps() throws MalformedURLException {
|
||||
public void testGenericMapOfMaps() {
|
||||
GenericBean<String> gb = new GenericBean<>();
|
||||
Map<String, Map<Integer, Long>> map = new HashMap<>();
|
||||
map.put("mykey", new HashMap<>());
|
||||
@@ -280,7 +276,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericMapOfMapsWithElementConversion() throws MalformedURLException {
|
||||
public void testGenericMapOfMapsWithElementConversion() {
|
||||
GenericBean<String> gb = new GenericBean<>();
|
||||
Map<String, Map<Integer, Long>> map = new HashMap<>();
|
||||
map.put("mykey", new HashMap<>());
|
||||
@@ -292,7 +288,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericMapOfLists() throws MalformedURLException {
|
||||
public void testGenericMapOfLists() {
|
||||
GenericBean<String> gb = new GenericBean<>();
|
||||
Map<Integer, List<Integer>> map = new HashMap<>();
|
||||
map.put(1, new LinkedList<>());
|
||||
@@ -304,7 +300,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericMapOfListsWithElementConversion() throws MalformedURLException {
|
||||
public void testGenericMapOfListsWithElementConversion() {
|
||||
GenericBean<String> gb = new GenericBean<>();
|
||||
Map<Integer, List<Integer>> map = new HashMap<>();
|
||||
map.put(1, new LinkedList<>());
|
||||
@@ -316,7 +312,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericTypeNestingMapOfInteger() throws Exception {
|
||||
public void testGenericTypeNestingMapOfInteger() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("testKey", "100");
|
||||
|
||||
@@ -325,14 +321,13 @@ public class BeanWrapperGenericsTests {
|
||||
bw.setPropertyValue("mapOfInteger", map);
|
||||
|
||||
Object obj = gb.getMapOfInteger().get("testKey");
|
||||
boolean condition = obj instanceof Integer;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(obj instanceof Integer).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericTypeNestingMapOfListOfInteger() throws Exception {
|
||||
public void testGenericTypeNestingMapOfListOfInteger() {
|
||||
Map<String, List<String>> map = new HashMap<>();
|
||||
List<String> list = Arrays.asList(new String[] {"1", "2", "3"});
|
||||
List<String> list = Arrays.asList("1", "2", "3");
|
||||
map.put("testKey", list);
|
||||
|
||||
NestedGenericCollectionBean gb = new NestedGenericCollectionBean();
|
||||
@@ -340,13 +335,12 @@ public class BeanWrapperGenericsTests {
|
||||
bw.setPropertyValue("mapOfListOfInteger", map);
|
||||
|
||||
Object obj = gb.getMapOfListOfInteger().get("testKey").get(0);
|
||||
boolean condition = obj instanceof Integer;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(obj instanceof Integer).isTrue();
|
||||
assertThat(((Integer) obj).intValue()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericTypeNestingListOfMapOfInteger() throws Exception {
|
||||
public void testGenericTypeNestingListOfMapOfInteger() {
|
||||
List<Map<String, String>> list = new LinkedList<>();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("testKey", "5");
|
||||
@@ -357,15 +351,14 @@ public class BeanWrapperGenericsTests {
|
||||
bw.setPropertyValue("listOfMapOfInteger", list);
|
||||
|
||||
Object obj = gb.getListOfMapOfInteger().get(0).get("testKey");
|
||||
boolean condition = obj instanceof Integer;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(obj instanceof Integer).isTrue();
|
||||
assertThat(((Integer) obj).intValue()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericTypeNestingMapOfListOfListOfInteger() throws Exception {
|
||||
public void testGenericTypeNestingMapOfListOfListOfInteger() {
|
||||
Map<String, List<List<String>>> map = new HashMap<>();
|
||||
List<String> list = Arrays.asList(new String[] {"1", "2", "3"});
|
||||
List<String> list = Arrays.asList("1", "2", "3");
|
||||
map.put("testKey", Collections.singletonList(list));
|
||||
|
||||
NestedGenericCollectionBean gb = new NestedGenericCollectionBean();
|
||||
@@ -373,8 +366,7 @@ public class BeanWrapperGenericsTests {
|
||||
bw.setPropertyValue("mapOfListOfListOfInteger", map);
|
||||
|
||||
Object obj = gb.getMapOfListOfListOfInteger().get("testKey").get(0).get(0);
|
||||
boolean condition = obj instanceof Integer;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(obj instanceof Integer).isTrue();
|
||||
assertThat(((Integer) obj).intValue()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@@ -465,7 +457,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericallyTypedIntegerBean() throws Exception {
|
||||
public void testGenericallyTypedIntegerBean() {
|
||||
GenericIntegerBean gb = new GenericIntegerBean();
|
||||
BeanWrapper bw = new BeanWrapperImpl(gb);
|
||||
bw.setPropertyValue("genericProperty", "10");
|
||||
@@ -476,7 +468,7 @@ public class BeanWrapperGenericsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericallyTypedSetOfIntegerBean() throws Exception {
|
||||
public void testGenericallyTypedSetOfIntegerBean() {
|
||||
GenericSetOfIntegerBean gb = new GenericSetOfIntegerBean();
|
||||
BeanWrapper bw = new BeanWrapperImpl(gb);
|
||||
bw.setPropertyValue("genericProperty", "10");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,9 @@ import java.util.Optional;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.core.OverridingClassLoader;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
@@ -104,7 +107,7 @@ public class BeanWrapperTests extends AbstractPropertyAccessorTests {
|
||||
.satisfies(ex -> assertThat(ex.getPossibleMatches()).containsExactly("age"));
|
||||
}
|
||||
|
||||
@Test // Can't be shared; there is no such thing as a read-only field
|
||||
@Test // Can't be shared; there is no such thing as a read-only field
|
||||
public void setReadOnlyMapProperty() {
|
||||
TypedReadOnlyMap map = new TypedReadOnlyMap(Collections.singletonMap("key", new TestBean()));
|
||||
TypedReadOnlyMapClient target = new TypedReadOnlyMapClient();
|
||||
@@ -150,18 +153,58 @@ public class BeanWrapperTests extends AbstractPropertyAccessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertyDescriptors() {
|
||||
public void propertyDescriptors() throws Exception {
|
||||
TestBean target = new TestBean();
|
||||
target.setSpouse(new TestBean());
|
||||
BeanWrapper accessor = createAccessor(target);
|
||||
accessor.setPropertyValue("name", "a");
|
||||
accessor.setPropertyValue("spouse.name", "b");
|
||||
|
||||
assertThat(target.getName()).isEqualTo("a");
|
||||
assertThat(target.getSpouse().getName()).isEqualTo("b");
|
||||
assertThat(accessor.getPropertyValue("name")).isEqualTo("a");
|
||||
assertThat(accessor.getPropertyValue("spouse.name")).isEqualTo("b");
|
||||
assertThat(accessor.getPropertyDescriptor("name").getPropertyType()).isEqualTo(String.class);
|
||||
assertThat(accessor.getPropertyDescriptor("spouse.name").getPropertyType()).isEqualTo(String.class);
|
||||
|
||||
assertThat(accessor.isReadableProperty("class.package")).isFalse();
|
||||
assertThat(accessor.isReadableProperty("class.module")).isFalse();
|
||||
assertThat(accessor.isReadableProperty("class.classLoader")).isFalse();
|
||||
assertThat(accessor.isReadableProperty("class.name")).isTrue();
|
||||
assertThat(accessor.isReadableProperty("class.simpleName")).isTrue();
|
||||
assertThat(accessor.getPropertyValue("class.name")).isEqualTo(TestBean.class.getName());
|
||||
assertThat(accessor.getPropertyValue("class.simpleName")).isEqualTo(TestBean.class.getSimpleName());
|
||||
assertThat(accessor.getPropertyDescriptor("class.name").getPropertyType()).isEqualTo(String.class);
|
||||
assertThat(accessor.getPropertyDescriptor("class.simpleName").getPropertyType()).isEqualTo(String.class);
|
||||
|
||||
accessor = createAccessor(new DefaultResourceLoader());
|
||||
|
||||
assertThat(accessor.isReadableProperty("class.package")).isFalse();
|
||||
assertThat(accessor.isReadableProperty("class.module")).isFalse();
|
||||
assertThat(accessor.isReadableProperty("class.classLoader")).isFalse();
|
||||
assertThat(accessor.isReadableProperty("class.name")).isTrue();
|
||||
assertThat(accessor.isReadableProperty("class.simpleName")).isTrue();
|
||||
assertThat(accessor.isReadableProperty("classLoader")).isTrue();
|
||||
assertThat(accessor.isWritableProperty("classLoader")).isTrue();
|
||||
OverridingClassLoader ocl = new OverridingClassLoader(getClass().getClassLoader());
|
||||
accessor.setPropertyValue("classLoader", ocl);
|
||||
assertThat(accessor.getPropertyValue("classLoader")).isSameAs(ocl);
|
||||
|
||||
accessor = createAccessor(new UrlResource("https://spring.io"));
|
||||
|
||||
assertThat(accessor.isReadableProperty("class.package")).isFalse();
|
||||
assertThat(accessor.isReadableProperty("class.module")).isFalse();
|
||||
assertThat(accessor.isReadableProperty("class.classLoader")).isFalse();
|
||||
assertThat(accessor.isReadableProperty("class.name")).isTrue();
|
||||
assertThat(accessor.isReadableProperty("class.simpleName")).isTrue();
|
||||
assertThat(accessor.isReadableProperty("URL.protocol")).isTrue();
|
||||
assertThat(accessor.isReadableProperty("URL.host")).isTrue();
|
||||
assertThat(accessor.isReadableProperty("URL.port")).isTrue();
|
||||
assertThat(accessor.isReadableProperty("URL.file")).isTrue();
|
||||
assertThat(accessor.isReadableProperty("URL.content")).isFalse();
|
||||
assertThat(accessor.isReadableProperty("inputStream")).isFalse();
|
||||
assertThat(accessor.isReadableProperty("filename")).isTrue();
|
||||
assertThat(accessor.isReadableProperty("description")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+16
-11
@@ -63,9 +63,8 @@ public class BeanFactoryUtilsTests {
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
public void setup() {
|
||||
// Interesting hierarchical factory to test counts.
|
||||
// Slow to read so we cache it.
|
||||
|
||||
DefaultListableBeanFactory grandParent = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(grandParent).loadBeanDefinitions(ROOT_CONTEXT);
|
||||
@@ -93,7 +92,7 @@ public class BeanFactoryUtilsTests {
|
||||
* Check that override doesn't count as two separate beans.
|
||||
*/
|
||||
@Test
|
||||
public void testHierarchicalCountBeansWithOverride() throws Exception {
|
||||
public void testHierarchicalCountBeansWithOverride() {
|
||||
// Leaf count
|
||||
assertThat(this.listableBeanFactory.getBeanDefinitionCount() == 1).isTrue();
|
||||
// Count minus duplicate
|
||||
@@ -101,14 +100,14 @@ public class BeanFactoryUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHierarchicalNamesWithNoMatch() throws Exception {
|
||||
public void testHierarchicalNamesWithNoMatch() {
|
||||
List<String> names = Arrays.asList(
|
||||
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.listableBeanFactory, NoOp.class));
|
||||
assertThat(names.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHierarchicalNamesWithMatchOnlyInRoot() throws Exception {
|
||||
public void testHierarchicalNamesWithMatchOnlyInRoot() {
|
||||
List<String> names = Arrays.asList(
|
||||
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.listableBeanFactory, IndexedTestBean.class));
|
||||
assertThat(names.size()).isEqualTo(1);
|
||||
@@ -118,7 +117,7 @@ public class BeanFactoryUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBeanNamesForTypeWithOverride() throws Exception {
|
||||
public void testGetBeanNamesForTypeWithOverride() {
|
||||
List<String> names = Arrays.asList(
|
||||
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.listableBeanFactory, ITestBean.class));
|
||||
// includes 2 TestBeans from FactoryBeans (DummyFactory definitions)
|
||||
@@ -236,7 +235,7 @@ public class BeanFactoryUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHierarchicalResolutionWithOverride() throws Exception {
|
||||
public void testHierarchicalResolutionWithOverride() {
|
||||
Object test3 = this.listableBeanFactory.getBean("test3");
|
||||
Object test = this.listableBeanFactory.getBean("test");
|
||||
|
||||
@@ -276,14 +275,14 @@ public class BeanFactoryUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHierarchicalNamesForAnnotationWithNoMatch() throws Exception {
|
||||
public void testHierarchicalNamesForAnnotationWithNoMatch() {
|
||||
List<String> names = Arrays.asList(
|
||||
BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.listableBeanFactory, Override.class));
|
||||
assertThat(names.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHierarchicalNamesForAnnotationWithMatchOnlyInRoot() throws Exception {
|
||||
public void testHierarchicalNamesForAnnotationWithMatchOnlyInRoot() {
|
||||
List<String> names = Arrays.asList(
|
||||
BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.listableBeanFactory, TestAnnotation.class));
|
||||
assertThat(names.size()).isEqualTo(1);
|
||||
@@ -293,7 +292,7 @@ public class BeanFactoryUtilsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBeanNamesForAnnotationWithOverride() throws Exception {
|
||||
public void testGetBeanNamesForAnnotationWithOverride() {
|
||||
AnnotatedBean annotatedBean = new AnnotatedBean();
|
||||
this.listableBeanFactory.registerSingleton("anotherAnnotatedBean", annotatedBean);
|
||||
List<String> names = Arrays.asList(
|
||||
@@ -433,6 +432,7 @@ public class BeanFactoryUtilsTests {
|
||||
String basePackage() default "";
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@ControllerAdvice
|
||||
@interface RestControllerAdvice {
|
||||
@@ -444,18 +444,23 @@ public class BeanFactoryUtilsTests {
|
||||
String basePackage() default "";
|
||||
}
|
||||
|
||||
|
||||
@ControllerAdvice("com.example")
|
||||
static class ControllerAdviceClass {
|
||||
}
|
||||
|
||||
|
||||
@RestControllerAdvice("com.example")
|
||||
static class RestControllerAdviceClass {
|
||||
}
|
||||
|
||||
|
||||
static class TestBeanSmartFactoryBean implements SmartFactoryBean<TestBean> {
|
||||
|
||||
private final TestBean testBean = new TestBean("enigma", 42);
|
||||
|
||||
private final boolean singleton;
|
||||
|
||||
private final boolean prototype;
|
||||
|
||||
TestBeanSmartFactoryBean(boolean singleton, boolean prototype) {
|
||||
@@ -478,7 +483,7 @@ public class BeanFactoryUtilsTests {
|
||||
return TestBean.class;
|
||||
}
|
||||
|
||||
public TestBean getObject() throws Exception {
|
||||
public TestBean getObject() {
|
||||
// We don't really care if the actual instance is a singleton or prototype
|
||||
// for the tests that use this factory.
|
||||
return this.testBean;
|
||||
|
||||
+16
-22
@@ -796,12 +796,13 @@ class DefaultListableBeanFactoryTests {
|
||||
factory.registerBeanDefinition("child", childDefinition);
|
||||
factory.registerAlias("parent", "alias");
|
||||
|
||||
TestBean child = (TestBean) factory.getBean("child");
|
||||
TestBean child = factory.getBean("child", TestBean.class);
|
||||
assertThat(child.getName()).isEqualTo(EXPECTED_NAME);
|
||||
assertThat(child.getAge()).isEqualTo(EXPECTED_AGE);
|
||||
Object mergedBeanDefinition2 = factory.getMergedBeanDefinition("child");
|
||||
BeanDefinition mergedBeanDefinition1 = factory.getMergedBeanDefinition("child");
|
||||
BeanDefinition mergedBeanDefinition2 = factory.getMergedBeanDefinition("child");
|
||||
|
||||
assertThat(mergedBeanDefinition2).as("Use cached merged bean definition").isEqualTo(mergedBeanDefinition2);
|
||||
assertThat(mergedBeanDefinition1).as("Use cached merged bean definition").isSameAs(mergedBeanDefinition2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1849,8 +1850,7 @@ class DefaultListableBeanFactoryTests {
|
||||
assertThat(factoryBean).as("The FactoryBean should have been registered.").isNotNull();
|
||||
FactoryBeanDependentBean bean = (FactoryBeanDependentBean) lbf.autowire(FactoryBeanDependentBean.class,
|
||||
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
|
||||
Object mergedBeanDefinition2 = bean.getFactoryBean();
|
||||
assertThat(mergedBeanDefinition2).as("The FactoryBeanDependentBean should have been autowired 'by type' with the LazyInitFactory.").isEqualTo(mergedBeanDefinition2);
|
||||
assertThat(bean.getFactoryBean()).as("The FactoryBeanDependentBean should have been autowired 'by type' with the LazyInitFactory.").isEqualTo(factoryBean);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2556,8 +2556,7 @@ class DefaultListableBeanFactoryTests {
|
||||
BeanWithDestroyMethod.closeCount = 0;
|
||||
lbf.preInstantiateSingletons();
|
||||
lbf.destroySingletons();
|
||||
Object mergedBeanDefinition2 = BeanWithDestroyMethod.closeCount;
|
||||
assertThat(mergedBeanDefinition2).as("Destroy methods invoked").isEqualTo(mergedBeanDefinition2);
|
||||
assertThat(BeanWithDestroyMethod.closeCount).as("Destroy methods invoked").isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2571,8 +2570,7 @@ class DefaultListableBeanFactoryTests {
|
||||
BeanWithDestroyMethod.closeCount = 0;
|
||||
lbf.preInstantiateSingletons();
|
||||
lbf.destroySingletons();
|
||||
Object mergedBeanDefinition2 = BeanWithDestroyMethod.closeCount;
|
||||
assertThat(mergedBeanDefinition2).as("Destroy methods invoked").isEqualTo(mergedBeanDefinition2);
|
||||
assertThat(BeanWithDestroyMethod.closeCount).as("Destroy methods invoked").isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2587,8 +2585,7 @@ class DefaultListableBeanFactoryTests {
|
||||
BeanWithDestroyMethod.closeCount = 0;
|
||||
lbf.preInstantiateSingletons();
|
||||
lbf.destroySingletons();
|
||||
Object mergedBeanDefinition2 = BeanWithDestroyMethod.closeCount;
|
||||
assertThat(mergedBeanDefinition2).as("Destroy methods invoked").isEqualTo(mergedBeanDefinition2);
|
||||
assertThat(BeanWithDestroyMethod.closeCount).as("Destroy methods invoked").isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2710,14 +2707,15 @@ class DefaultListableBeanFactoryTests {
|
||||
factory.registerBeanDefinition("child", child);
|
||||
|
||||
AbstractBeanDefinition def = (AbstractBeanDefinition) factory.getBeanDefinition("child");
|
||||
Object mergedBeanDefinition2 = def.getScope();
|
||||
assertThat(mergedBeanDefinition2).as("Child 'scope' not overriding parent scope (it must).").isEqualTo(mergedBeanDefinition2);
|
||||
assertThat(def.getScope()).as("Child 'scope' not overriding parent scope (it must).").isEqualTo(theChildScope);
|
||||
}
|
||||
|
||||
@Test
|
||||
void scopeInheritanceForChildBeanDefinitions() {
|
||||
String theParentScope = "bonanza!";
|
||||
|
||||
RootBeanDefinition parent = new RootBeanDefinition();
|
||||
parent.setScope("bonanza!");
|
||||
parent.setScope(theParentScope);
|
||||
|
||||
AbstractBeanDefinition child = new ChildBeanDefinition("parent");
|
||||
child.setBeanClass(TestBean.class);
|
||||
@@ -2727,8 +2725,7 @@ class DefaultListableBeanFactoryTests {
|
||||
factory.registerBeanDefinition("child", child);
|
||||
|
||||
BeanDefinition def = factory.getMergedBeanDefinition("child");
|
||||
Object mergedBeanDefinition2 = def.getScope();
|
||||
assertThat(mergedBeanDefinition2).as("Child 'scope' not inherited").isEqualTo(mergedBeanDefinition2);
|
||||
assertThat(def.getScope()).as("Child 'scope' not inherited").isEqualTo(theParentScope);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2764,15 +2761,12 @@ class DefaultListableBeanFactoryTests {
|
||||
});
|
||||
lbf.preInstantiateSingletons();
|
||||
TestBean tb = (TestBean) lbf.getBean("test");
|
||||
Object mergedBeanDefinition2 = tb.getName();
|
||||
assertThat(mergedBeanDefinition2).as("Name was set on field by IAPP").isEqualTo(mergedBeanDefinition2);
|
||||
assertThat(tb.getName()).as("Name was set on field by IAPP").isEqualTo(nameSetOnField);
|
||||
if (!skipPropertyPopulation) {
|
||||
Object mergedBeanDefinition21 = tb.getAge();
|
||||
assertThat(mergedBeanDefinition21).as("Property value still set").isEqualTo(mergedBeanDefinition21);
|
||||
assertThat(tb.getAge()).as("Property value still set").isEqualTo(ageSetByPropertyValue);
|
||||
}
|
||||
else {
|
||||
Object mergedBeanDefinition21 = tb.getAge();
|
||||
assertThat(mergedBeanDefinition21).as("Property value was NOT set and still has default value").isEqualTo(mergedBeanDefinition21);
|
||||
assertThat(tb.getAge()).as("Property value was NOT set and still has default value").isEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -31,6 +31,7 @@ import static org.springframework.beans.factory.support.BeanDefinitionBuilder.ro
|
||||
* invoking a factory method is not instructive to the user and rather misleading.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class Spr5475Tests {
|
||||
|
||||
@@ -40,7 +41,8 @@ public class Spr5475Tests {
|
||||
rootBeanDefinition(Foo.class)
|
||||
.setFactoryMethod("noArgFactory")
|
||||
.addConstructorArgValue("bogusArg").getBeanDefinition(),
|
||||
"Error creating bean with name 'foo': No matching factory method found: factory method 'noArgFactory(String)'. " +
|
||||
"Error creating bean with name 'foo': No matching factory method found on class " +
|
||||
"[org.springframework.beans.factory.Spr5475Tests$Foo]: factory method 'noArgFactory(String)'. " +
|
||||
"Check that a method with the specified name and arguments exists and that it is static.");
|
||||
}
|
||||
|
||||
@@ -51,7 +53,8 @@ public class Spr5475Tests {
|
||||
.setFactoryMethod("noArgFactory")
|
||||
.addConstructorArgValue("bogusArg1")
|
||||
.addConstructorArgValue("bogusArg2".getBytes()).getBeanDefinition(),
|
||||
"Error creating bean with name 'foo': No matching factory method found: factory method 'noArgFactory(String,byte[])'. " +
|
||||
"Error creating bean with name 'foo': No matching factory method found on class " +
|
||||
"[org.springframework.beans.factory.Spr5475Tests$Foo]: factory method 'noArgFactory(String,byte[])'. " +
|
||||
"Check that a method with the specified name and arguments exists and that it is static.");
|
||||
}
|
||||
|
||||
@@ -65,7 +68,8 @@ public class Spr5475Tests {
|
||||
def.setConstructorArgumentValues(cav);
|
||||
|
||||
assertExceptionMessageForMisconfiguredFactoryMethod(def,
|
||||
"Error creating bean with name 'foo': No matching factory method found: factory method 'noArgFactory(CharSequence,byte[])'. " +
|
||||
"Error creating bean with name 'foo': No matching factory method found on class " +
|
||||
"[org.springframework.beans.factory.Spr5475Tests$Foo]: factory method 'noArgFactory(CharSequence,byte[])'. " +
|
||||
"Check that a method with the specified name and arguments exists and that it is static.");
|
||||
}
|
||||
|
||||
|
||||
+193
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -297,6 +297,121 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionalResourceInjectionWithSingletonRemoval() {
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(OptionalResourceInjectionBean.class);
|
||||
rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", rbd);
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
IndexedTestBean itb = new IndexedTestBean();
|
||||
bf.registerSingleton("indexedTestBean", itb);
|
||||
NestedTestBean ntb1 = new NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean1", ntb1);
|
||||
NestedTestBean ntb2 = new NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean2", ntb2);
|
||||
|
||||
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean2()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean3()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean4()).isSameAs(tb);
|
||||
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
|
||||
assertThat(bean.getNestedTestBeans().length).isEqualTo(2);
|
||||
assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1);
|
||||
assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2);
|
||||
assertThat(bean.nestedTestBeansField.length).isEqualTo(2);
|
||||
assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1);
|
||||
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
|
||||
|
||||
bf.destroySingleton("testBean");
|
||||
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
assertThat(bean.getTestBean2()).isNull();
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
assertThat(bean.getTestBean4()).isNull();
|
||||
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
|
||||
assertThat(bean.getNestedTestBeans().length).isEqualTo(2);
|
||||
assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1);
|
||||
assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2);
|
||||
assertThat(bean.nestedTestBeansField.length).isEqualTo(2);
|
||||
assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1);
|
||||
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
|
||||
|
||||
bf.registerSingleton("testBean", tb);
|
||||
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean2()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean3()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean4()).isSameAs(tb);
|
||||
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
|
||||
assertThat(bean.getNestedTestBeans().length).isEqualTo(2);
|
||||
assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1);
|
||||
assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2);
|
||||
assertThat(bean.nestedTestBeansField.length).isEqualTo(2);
|
||||
assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1);
|
||||
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionalResourceInjectionWithBeanDefinitionRemoval() {
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(OptionalResourceInjectionBean.class);
|
||||
rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", rbd);
|
||||
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
|
||||
IndexedTestBean itb = new IndexedTestBean();
|
||||
bf.registerSingleton("indexedTestBean", itb);
|
||||
NestedTestBean ntb1 = new NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean1", ntb1);
|
||||
NestedTestBean ntb2 = new NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean2", ntb2);
|
||||
|
||||
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
|
||||
assertThat(bean.getTestBean2()).isSameAs(bf.getBean("testBean"));
|
||||
assertThat(bean.getTestBean3()).isSameAs(bf.getBean("testBean"));
|
||||
assertThat(bean.getTestBean4()).isSameAs(bf.getBean("testBean"));
|
||||
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
|
||||
assertThat(bean.getNestedTestBeans().length).isEqualTo(2);
|
||||
assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1);
|
||||
assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2);
|
||||
assertThat(bean.nestedTestBeansField.length).isEqualTo(2);
|
||||
assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1);
|
||||
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
|
||||
|
||||
bf.removeBeanDefinition("testBean");
|
||||
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isNull();
|
||||
assertThat(bean.getTestBean2()).isNull();
|
||||
assertThat(bean.getTestBean3()).isNull();
|
||||
assertThat(bean.getTestBean4()).isNull();
|
||||
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
|
||||
assertThat(bean.getNestedTestBeans().length).isEqualTo(2);
|
||||
assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1);
|
||||
assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2);
|
||||
assertThat(bean.nestedTestBeansField.length).isEqualTo(2);
|
||||
assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1);
|
||||
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
|
||||
|
||||
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
|
||||
|
||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
|
||||
assertThat(bean.getTestBean2()).isSameAs(bf.getBean("testBean"));
|
||||
assertThat(bean.getTestBean3()).isSameAs(bf.getBean("testBean"));
|
||||
assertThat(bean.getTestBean4()).isSameAs(bf.getBean("testBean"));
|
||||
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
|
||||
assertThat(bean.getNestedTestBeans().length).isEqualTo(2);
|
||||
assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1);
|
||||
assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2);
|
||||
assertThat(bean.nestedTestBeansField.length).isEqualTo(2);
|
||||
assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1);
|
||||
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptionalCollectionResourceInjection() {
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(OptionalCollectionResourceInjectionBean.class);
|
||||
@@ -533,6 +648,83 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorResourceInjectionWithSingletonRemoval() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
NestedTestBean ntb = new NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean", ntb);
|
||||
|
||||
ConstructorResourceInjectionBean bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean2()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean3()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean4()).isSameAs(tb);
|
||||
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
|
||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||
|
||||
bf.destroySingleton("nestedTestBean");
|
||||
|
||||
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean2()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean3()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean4()).isSameAs(tb);
|
||||
assertThat(bean.getNestedTestBean()).isNull();
|
||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||
|
||||
bf.registerSingleton("nestedTestBean", ntb);
|
||||
|
||||
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean2()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean3()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean4()).isSameAs(tb);
|
||||
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
|
||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorResourceInjectionWithBeanDefinitionRemoval() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class);
|
||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
bf.registerBeanDefinition("nestedTestBean", new RootBeanDefinition(NestedTestBean.class));
|
||||
|
||||
ConstructorResourceInjectionBean bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean2()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean3()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean4()).isSameAs(tb);
|
||||
assertThat(bean.getNestedTestBean()).isSameAs(bf.getBean("nestedTestBean"));
|
||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||
|
||||
bf.removeBeanDefinition("nestedTestBean");
|
||||
|
||||
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean2()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean3()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean4()).isSameAs(tb);
|
||||
assertThat(bean.getNestedTestBean()).isNull();
|
||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||
|
||||
bf.registerBeanDefinition("nestedTestBean", new RootBeanDefinition(NestedTestBean.class));
|
||||
|
||||
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean2()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean3()).isSameAs(tb);
|
||||
assertThat(bean.getTestBean4()).isSameAs(tb);
|
||||
assertThat(bean.getNestedTestBean()).isSameAs(bf.getBean("nestedTestBean"));
|
||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorResourceInjectionWithNullFromFactoryBean() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class);
|
||||
|
||||
+11
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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,16 @@ public class BeanDefinitionTests {
|
||||
assertThat(bd.equals(otherBd)).isTrue();
|
||||
assertThat(otherBd.equals(bd)).isTrue();
|
||||
assertThat(bd.hashCode() == otherBd.hashCode()).isTrue();
|
||||
|
||||
bd.getPropertyValues();
|
||||
assertThat(bd.equals(otherBd)).isTrue();
|
||||
assertThat(otherBd.equals(bd)).isTrue();
|
||||
assertThat(bd.hashCode() == otherBd.hashCode()).isTrue();
|
||||
|
||||
bd.getConstructorArgumentValues();
|
||||
assertThat(bd.equals(otherBd)).isTrue();
|
||||
assertThat(otherBd.equals(bd)).isTrue();
|
||||
assertThat(bd.hashCode() == otherBd.hashCode()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -244,7 +244,7 @@ public class QualifierAnnotationAutowireBeanFactoryTests {
|
||||
@Target({ElementType.FIELD, ElementType.PARAMETER})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Qualifier
|
||||
private static @interface TestQualifier {
|
||||
private @interface TestQualifier {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+41
-21
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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,57 +34,78 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
public class PathEditorTests {
|
||||
|
||||
@Test
|
||||
public void testClasspathPathName() throws Exception {
|
||||
public void testClasspathPathName() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".class");
|
||||
Object value = pathEditor.getValue();
|
||||
boolean condition = value instanceof Path;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
Path path = (Path) value;
|
||||
assertThat(path.toFile().exists()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNonExistentResource() throws Exception {
|
||||
public void testWithNonExistentResource() {
|
||||
PropertyEditor propertyEditor = new PathEditor();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
propertyEditor.setAsText("classpath:/no_way_this_file_is_found.doc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNonExistentPath() throws Exception {
|
||||
public void testWithNonExistentPath() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("file:/no_way_this_file_is_found.doc");
|
||||
Object value = pathEditor.getValue();
|
||||
boolean condition1 = value instanceof Path;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
Path path = (Path) value;
|
||||
boolean condition = !path.toFile().exists();
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(!path.toFile().exists()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbsolutePath() throws Exception {
|
||||
public void testAbsolutePath() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("/no_way_this_file_is_found.doc");
|
||||
Object value = pathEditor.getValue();
|
||||
boolean condition1 = value instanceof Path;
|
||||
assertThat(condition1).isTrue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
Path path = (Path) value;
|
||||
boolean condition = !path.toFile().exists();
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(!path.toFile().exists()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnqualifiedPathNameFound() throws Exception {
|
||||
public void testWindowsAbsolutePath() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
pathEditor.setAsText("C:\\no_way_this_file_is_found.doc");
|
||||
Object value = pathEditor.getValue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
Path path = (Path) value;
|
||||
assertThat(!path.toFile().exists()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowsAbsoluteFilePath() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
try {
|
||||
pathEditor.setAsText("file://C:\\no_way_this_file_is_found.doc");
|
||||
Object value = pathEditor.getValue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
Path path = (Path) value;
|
||||
assertThat(!path.toFile().exists()).isTrue();
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
if (File.separatorChar == '\\') { // on Windows, otherwise silently ignore
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnqualifiedPathNameFound() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".class";
|
||||
pathEditor.setAsText(fileName);
|
||||
Object value = pathEditor.getValue();
|
||||
boolean condition = value instanceof Path;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
Path path = (Path) value;
|
||||
File file = path.toFile();
|
||||
assertThat(file.exists()).isTrue();
|
||||
@@ -96,14 +117,13 @@ public class PathEditorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnqualifiedPathNameNotFound() throws Exception {
|
||||
public void testUnqualifiedPathNameNotFound() {
|
||||
PropertyEditor pathEditor = new PathEditor();
|
||||
String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
|
||||
ClassUtils.getShortName(getClass()) + ".clazz";
|
||||
pathEditor.setAsText(fileName);
|
||||
Object value = pathEditor.getValue();
|
||||
boolean condition = value instanceof Path;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(value instanceof Path).isTrue();
|
||||
Path path = (Path) value;
|
||||
File file = path.toFile();
|
||||
assertThat(file.exists()).isFalse();
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,7 +22,8 @@ import javax.lang.model.element.Element;
|
||||
|
||||
/**
|
||||
* Provide the list of stereotypes that match an {@link Element}.
|
||||
* If an element has one more stereotypes, it is referenced in the index
|
||||
*
|
||||
* <p>If an element has one or more stereotypes, it is referenced in the index
|
||||
* of candidate components and each stereotype can be queried individually.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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,6 +58,8 @@ import org.springframework.lang.Nullable;
|
||||
* @since 1.1
|
||||
* @see SchedulerFactoryBean#setDataSource
|
||||
* @see SchedulerFactoryBean#setNonTransactionalDataSource
|
||||
* @see SchedulerFactoryBean#getConfigTimeDataSource()
|
||||
* @see SchedulerFactoryBean#getConfigTimeNonTransactionalDataSource()
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
|
||||
*/
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -313,6 +313,7 @@ public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBe
|
||||
* If set, this will override corresponding settings in Quartz properties.
|
||||
* <p>Note: If this is set, the Quartz settings should not define
|
||||
* a job store "dataSource" to avoid meaningless double configuration.
|
||||
* Also, do not define a "org.quartz.jobStore.class" property at all.
|
||||
* <p>A Spring-specific subclass of Quartz' JobStoreCMT will be used.
|
||||
* It is therefore strongly recommended to perform all operations on
|
||||
* the Scheduler within Spring-managed (or plain JTA) transactions.
|
||||
|
||||
@@ -34,8 +34,8 @@ dependencies {
|
||||
testCompile("org.codehaus.groovy:groovy-test")
|
||||
testCompile("org.codehaus.groovy:groovy-xml")
|
||||
testCompile("org.apache.commons:commons-pool2")
|
||||
testCompile("javax.inject:javax.inject-tck")
|
||||
testCompile("org.awaitility:awaitility")
|
||||
testCompile("javax.inject:javax.inject-tck")
|
||||
testRuntime("javax.xml.bind:jaxb-api")
|
||||
testRuntime("org.glassfish:javax.el")
|
||||
// Substitute for javax.management:jmxremote_optional:1.0.1_04 (not available on Maven Central)
|
||||
|
||||
+8
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -31,7 +31,8 @@ import org.springframework.core.annotation.AliasFor;
|
||||
*
|
||||
* <p>In contrast to the {@link Cacheable @Cacheable} annotation, this annotation
|
||||
* does not cause the advised method to be skipped. Rather, it always causes the
|
||||
* method to be invoked and its result to be stored in the associated cache. Note
|
||||
* method to be invoked and its result to be stored in the associated cache if the
|
||||
* {@link #condition()} and {@link #unless()} expressions match accordingly. Note
|
||||
* that Java8's {@code Optional} return types are automatically handled and its
|
||||
* content is stored in the cache if present.
|
||||
*
|
||||
@@ -118,10 +119,15 @@ public @interface CachePut {
|
||||
/**
|
||||
* Spring Expression Language (SpEL) expression used for making the cache
|
||||
* put operation conditional.
|
||||
* <p>This expression is evaluated after the method has been called due to the
|
||||
* nature of the put operation and can therefore refer to the {@code result}.
|
||||
* <p>Default is {@code ""}, meaning the method result is always cached.
|
||||
* <p>The SpEL expression evaluates against a dedicated context that provides the
|
||||
* following meta-data:
|
||||
* <ul>
|
||||
* <li>{@code #result} for a reference to the result of the method invocation. For
|
||||
* supported wrappers such as {@code Optional}, {@code #result} refers to the actual
|
||||
* object, not the wrapper</li>
|
||||
* <li>{@code #root.method}, {@code #root.target}, and {@code #root.caches} for
|
||||
* references to the {@link java.lang.reflect.Method method}, target object, and
|
||||
* affected cache(s) respectively.</li>
|
||||
@@ -136,8 +142,6 @@ public @interface CachePut {
|
||||
|
||||
/**
|
||||
* Spring Expression Language (SpEL) expression used to veto the cache put operation.
|
||||
* <p>Unlike {@link #condition}, this expression is evaluated after the method
|
||||
* has been called and can therefore refer to the {@code result}.
|
||||
* <p>Default is {@code ""}, meaning that caching is never vetoed.
|
||||
* <p>The SpEL expression evaluates against a dedicated context that provides the
|
||||
* following meta-data:
|
||||
|
||||
+15
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -244,17 +244,21 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
||||
|
||||
private static void registerCacheAspect(Element element, ParserContext parserContext) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(CacheManagementConfigUtils.JCACHE_ASPECT_BEAN_NAME)) {
|
||||
Object eleSource = parserContext.extractSource(element);
|
||||
RootBeanDefinition def = new RootBeanDefinition();
|
||||
def.setBeanClassName(JCACHE_ASPECT_CLASS_NAME);
|
||||
def.setFactoryMethodName("aspectOf");
|
||||
BeanDefinition sourceDef = createJCacheOperationSourceBeanDefinition(element, eleSource);
|
||||
String sourceName =
|
||||
parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
|
||||
def.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName));
|
||||
Object source = parserContext.extractSource(element);
|
||||
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(sourceDef, sourceName));
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(def, CacheManagementConfigUtils.JCACHE_ASPECT_BEAN_NAME));
|
||||
BeanDefinition cacheOperationSourceDef = createJCacheOperationSourceBeanDefinition(element, source);
|
||||
String cacheOperationSourceName = parserContext.getReaderContext().registerWithGeneratedName(cacheOperationSourceDef);
|
||||
|
||||
RootBeanDefinition jcacheAspectDef = new RootBeanDefinition();
|
||||
jcacheAspectDef.setBeanClassName(JCACHE_ASPECT_CLASS_NAME);
|
||||
jcacheAspectDef.setFactoryMethodName("aspectOf");
|
||||
jcacheAspectDef.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(cacheOperationSourceName));
|
||||
parserContext.getRegistry().registerBeanDefinition(CacheManagementConfigUtils.JCACHE_ASPECT_BEAN_NAME, jcacheAspectDef);
|
||||
|
||||
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(cacheOperationSourceDef, cacheOperationSourceName));
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(jcacheAspectDef, CacheManagementConfigUtils.JCACHE_ASPECT_BEAN_NAME));
|
||||
parserContext.registerComponent(compositeDef);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+26
-27
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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,11 +16,12 @@
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -93,9 +94,9 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
|
||||
private String resourcePattern = DEFAULT_RESOURCE_PATTERN;
|
||||
|
||||
private final List<TypeFilter> includeFilters = new LinkedList<>();
|
||||
private final List<TypeFilter> includeFilters = new ArrayList<>();
|
||||
|
||||
private final List<TypeFilter> excludeFilters = new LinkedList<>();
|
||||
private final List<TypeFilter> excludeFilters = new ArrayList<>();
|
||||
|
||||
@Nullable
|
||||
private Environment environment;
|
||||
@@ -424,40 +425,38 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
if (traceEnabled) {
|
||||
logger.trace("Scanning " + resource);
|
||||
}
|
||||
if (resource.isReadable()) {
|
||||
try {
|
||||
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
|
||||
if (isCandidateComponent(metadataReader)) {
|
||||
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
|
||||
sbd.setSource(resource);
|
||||
if (isCandidateComponent(sbd)) {
|
||||
if (debugEnabled) {
|
||||
logger.debug("Identified candidate component class: " + resource);
|
||||
}
|
||||
candidates.add(sbd);
|
||||
}
|
||||
else {
|
||||
if (debugEnabled) {
|
||||
logger.debug("Ignored because not a concrete top-level class: " + resource);
|
||||
}
|
||||
try {
|
||||
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
|
||||
if (isCandidateComponent(metadataReader)) {
|
||||
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
|
||||
sbd.setSource(resource);
|
||||
if (isCandidateComponent(sbd)) {
|
||||
if (debugEnabled) {
|
||||
logger.debug("Identified candidate component class: " + resource);
|
||||
}
|
||||
candidates.add(sbd);
|
||||
}
|
||||
else {
|
||||
if (traceEnabled) {
|
||||
logger.trace("Ignored because not matching any filter: " + resource);
|
||||
if (debugEnabled) {
|
||||
logger.debug("Ignored because not a concrete top-level class: " + resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanDefinitionStoreException(
|
||||
"Failed to read candidate component class: " + resource, ex);
|
||||
else {
|
||||
if (traceEnabled) {
|
||||
logger.trace("Ignored because not matching any filter: " + resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
catch (FileNotFoundException ex) {
|
||||
if (traceEnabled) {
|
||||
logger.trace("Ignored because not readable: " + resource);
|
||||
logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanDefinitionStoreException(
|
||||
"Failed to read candidate component class: " + resource, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -332,7 +332,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
}
|
||||
|
||||
|
||||
private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) {
|
||||
private InjectionMetadata findResourceMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
|
||||
// Fall back to class name as cache key, for backwards compatibility with custom callers.
|
||||
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
|
||||
// Quick check on the concurrent map first, with minimal locking.
|
||||
@@ -352,7 +352,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
|
||||
private InjectionMetadata buildResourceMetadata(Class<?> clazz) {
|
||||
if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {
|
||||
return InjectionMetadata.EMPTY;
|
||||
}
|
||||
@@ -464,6 +464,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
public void releaseTarget(Object target) {
|
||||
}
|
||||
};
|
||||
|
||||
ProxyFactory pf = new ProxyFactory();
|
||||
pf.setTargetSource(ts);
|
||||
if (element.lookupType.isInterface()) {
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -73,7 +73,7 @@ class ComponentScanAnnotationParser {
|
||||
}
|
||||
|
||||
|
||||
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
|
||||
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {
|
||||
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
|
||||
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
|
||||
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -453,8 +453,8 @@ class ConfigurationClassEnhancer {
|
||||
* instance directly. If a FactoryBean instance is fetched through the container via &-dereferencing,
|
||||
* it will not be proxied. This too is aligned with the way XML configuration works.
|
||||
*/
|
||||
private Object enhanceFactoryBean(final Object factoryBean, Class<?> exposedType,
|
||||
final ConfigurableBeanFactory beanFactory, final String beanName) {
|
||||
private Object enhanceFactoryBean(Object factoryBean, Class<?> exposedType,
|
||||
ConfigurableBeanFactory beanFactory, String beanName) {
|
||||
|
||||
try {
|
||||
Class<?> clazz = factoryBean.getClass();
|
||||
@@ -489,8 +489,8 @@ class ConfigurationClassEnhancer {
|
||||
return createCglibProxyForFactoryBean(factoryBean, beanFactory, beanName);
|
||||
}
|
||||
|
||||
private Object createInterfaceProxyForFactoryBean(final Object factoryBean, Class<?> interfaceType,
|
||||
final ConfigurableBeanFactory beanFactory, final String beanName) {
|
||||
private Object createInterfaceProxyForFactoryBean(Object factoryBean, Class<?> interfaceType,
|
||||
ConfigurableBeanFactory beanFactory, String beanName) {
|
||||
|
||||
return Proxy.newProxyInstance(
|
||||
factoryBean.getClass().getClassLoader(), new Class<?>[] {interfaceType},
|
||||
@@ -502,8 +502,8 @@ class ConfigurationClassEnhancer {
|
||||
});
|
||||
}
|
||||
|
||||
private Object createCglibProxyForFactoryBean(final Object factoryBean,
|
||||
final ConfigurableBeanFactory beanFactory, final String beanName) {
|
||||
private Object createCglibProxyForFactoryBean(Object factoryBean,
|
||||
ConfigurableBeanFactory beanFactory, String beanName) {
|
||||
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(factoryBean.getClass());
|
||||
|
||||
+22
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -71,16 +71,17 @@ import org.springframework.util.ClassUtils;
|
||||
*
|
||||
* <p>Registered by default when using {@code <context:annotation-config/>} or
|
||||
* {@code <context:component-scan/>}. Otherwise, may be declared manually as
|
||||
* with any other BeanFactoryPostProcessor.
|
||||
* with any other {@link BeanFactoryPostProcessor}.
|
||||
*
|
||||
* <p>This post processor is priority-ordered as it is important that any
|
||||
* {@link Bean} methods declared in {@code @Configuration} classes have
|
||||
* {@link Bean @Bean} methods declared in {@code @Configuration} classes have
|
||||
* their corresponding bean definitions registered before any other
|
||||
* {@link BeanFactoryPostProcessor} executes.
|
||||
* {@code BeanFactoryPostProcessor} executes.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
|
||||
@@ -376,21 +377,30 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
|
||||
AnnotationMetadata annotationMetadata = null;
|
||||
MethodMetadata methodMetadata = null;
|
||||
if (beanDef instanceof AnnotatedBeanDefinition) {
|
||||
methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
|
||||
AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDef;
|
||||
annotationMetadata = annotatedBeanDefinition.getMetadata();
|
||||
methodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();
|
||||
}
|
||||
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
|
||||
// Configuration class (full or lite) or a configuration-derived @Bean method
|
||||
// -> resolve bean class at this point...
|
||||
// -> eagerly resolve bean class at this point, unless it's a 'lite' configuration
|
||||
// or component class without @Bean methods.
|
||||
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
|
||||
if (!abd.hasBeanClass()) {
|
||||
try {
|
||||
abd.resolveBeanClass(this.beanClassLoader);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
|
||||
boolean liteConfigurationCandidateWithoutBeanMethods =
|
||||
(ConfigurationClassUtils.CONFIGURATION_CLASS_LITE.equals(configClassAttr) &&
|
||||
annotationMetadata != null && !ConfigurationClassUtils.hasBeanMethods(annotationMetadata));
|
||||
if (!liteConfigurationCandidateWithoutBeanMethods) {
|
||||
try {
|
||||
abd.resolveBeanClass(this.beanClassLoader);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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 @@ import org.springframework.stereotype.Component;
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 3.1
|
||||
*/
|
||||
abstract class ConfigurationClassUtils {
|
||||
@@ -162,6 +163,10 @@ abstract class ConfigurationClassUtils {
|
||||
}
|
||||
|
||||
// Finally, let's look for @Bean methods...
|
||||
return hasBeanMethods(metadata);
|
||||
}
|
||||
|
||||
static boolean hasBeanMethods(AnnotationMetadata metadata) {
|
||||
try {
|
||||
return metadata.hasAnnotatedMethods(Bean.class.getName());
|
||||
}
|
||||
|
||||
+9
@@ -43,6 +43,15 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class FullyQualifiedAnnotationBeanNameGenerator extends AnnotationBeanNameGenerator {
|
||||
|
||||
/**
|
||||
* A convenient constant for a default {@code FullyQualifiedAnnotationBeanNameGenerator}
|
||||
* instance, as used for configuration-level import purposes.
|
||||
* @since 5.2.11
|
||||
*/
|
||||
public static final FullyQualifiedAnnotationBeanNameGenerator INSTANCE =
|
||||
new FullyQualifiedAnnotationBeanNameGenerator();
|
||||
|
||||
|
||||
@Override
|
||||
protected String buildDefaultBeanName(BeanDefinition definition) {
|
||||
String beanClassName = definition.getBeanClassName();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2021 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,6 +47,11 @@ import java.lang.annotation.Target;
|
||||
* or {@link javax.inject.Inject}: In that context, it leads to the creation of a
|
||||
* lazy-resolution proxy for all affected dependencies, as an alternative to using
|
||||
* {@link org.springframework.beans.factory.ObjectFactory} or {@link javax.inject.Provider}.
|
||||
* Please note that such a lazy-resolution proxy will always be injected; if the target
|
||||
* dependency does not exist, you will only be able to find out through an exception on
|
||||
* invocation. As a consequence, such an injection point results in unintuitive behavior
|
||||
* for optional dependencies. For a programmatic equivalent, allowing for lazy references
|
||||
* with more sophistication, consider {@link org.springframework.beans.factory.ObjectProvider}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
|
||||
@@ -83,6 +83,7 @@ import org.springframework.core.annotation.AliasFor;
|
||||
* @author Sam Brannen
|
||||
* @since 4.2
|
||||
* @see EventListenerMethodProcessor
|
||||
* @see org.springframework.transaction.event.TransactionalEventListener
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -81,7 +81,7 @@ public class SourceFilteringListener implements GenericApplicationListener, Smar
|
||||
|
||||
@Override
|
||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||
return supportsEventType(ResolvableType.forType(eventType));
|
||||
return supportsEventType(ResolvableType.forClass(eventType));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,13 +48,13 @@ public final class CandidateComponentsIndexLoader {
|
||||
public static final String COMPONENTS_RESOURCE_LOCATION = "META-INF/spring.components";
|
||||
|
||||
/**
|
||||
* System property that instructs Spring to ignore the index, i.e.
|
||||
* System property that instructs Spring to ignore the components index, i.e.
|
||||
* to always return {@code null} from {@link #loadIndex(ClassLoader)}.
|
||||
* <p>The default is "false", allowing for regular use of the index. Switching this
|
||||
* flag to {@code true} fulfills a corner case scenario when an index is partially
|
||||
* available for some libraries (or use cases) but couldn't be built for the whole
|
||||
* application. In this case, the application context fallbacks to a regular
|
||||
* classpath arrangement (i.e. as no index was present at all).
|
||||
* classpath arrangement (i.e. as though no index were present at all).
|
||||
*/
|
||||
public static final String IGNORE_INDEX = "spring.index.ignore";
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -856,8 +856,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
|
||||
}
|
||||
|
||||
// Register a default embedded value resolver if no bean post-processor
|
||||
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
|
||||
// Register a default embedded value resolver if no BeanFactoryPostProcessor
|
||||
// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
|
||||
// at this point, primarily for resolution in annotation attribute values.
|
||||
if (!beanFactory.hasEmbeddedValueResolver()) {
|
||||
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
|
||||
|
||||
+10
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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,14 +36,17 @@ import java.lang.annotation.Target;
|
||||
*
|
||||
* <p>For ISO-based formatting, set the {@link #iso} attribute to be the desired {@link ISO} format,
|
||||
* such as {@link ISO#DATE}. For custom formatting, set the {@link #pattern} attribute to be the
|
||||
* DateTime pattern, such as {@code yyyy/MM/dd hh:mm:ss a}.
|
||||
* DateTime pattern, such as {@code "yyyy/MM/dd hh:mm:ss a"}.
|
||||
*
|
||||
* <p>Each attribute is mutually exclusive, so only set one attribute per annotation instance
|
||||
* (the one most convenient one for your formatting needs).
|
||||
* When the pattern attribute is specified, it takes precedence over both the style and ISO attribute.
|
||||
* When the {@link #iso} attribute is specified, it takes precedence over the style attribute.
|
||||
* When no annotation attributes are specified, the default format applied is style-based
|
||||
* with a style code of 'SS' (short date, short time).
|
||||
* (the one most convenient for your formatting needs).
|
||||
*
|
||||
* <ul>
|
||||
* <li>When the pattern attribute is specified, it takes precedence over both the style and ISO attribute.</li>
|
||||
* <li>When the {@link #iso} attribute is specified, it takes precedence over the style attribute.</li>
|
||||
* <li>When no annotation attributes are specified, the default format applied is style-based
|
||||
* with a style code of 'SS' (short date, short time).</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
@@ -106,7 +109,6 @@ public @interface DateTimeFormat {
|
||||
/**
|
||||
* The most common ISO DateTime Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSXXX},
|
||||
* e.g. "2000-10-31T01:30:00.000-05:00".
|
||||
* <p>This is the default if no annotation value is specified.
|
||||
*/
|
||||
DATE_TIME,
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -66,7 +66,8 @@ public class ConcurrentModel extends ConcurrentHashMap<String, Object> implement
|
||||
|
||||
|
||||
@Override
|
||||
public Object put(String key, Object value) {
|
||||
@Nullable
|
||||
public Object put(String key, @Nullable Object value) {
|
||||
if (value != null) {
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -51,18 +51,20 @@ import org.springframework.util.PatternMatchUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Binder that allows for setting property values onto a target object,
|
||||
* including support for validation and binding result analysis.
|
||||
* The binding process can be customized through specifying allowed fields,
|
||||
* Binder that allows for setting property values on a target object, including
|
||||
* support for validation and binding result analysis.
|
||||
*
|
||||
* <p>The binding process can be customized by specifying allowed field patterns,
|
||||
* required fields, custom editors, etc.
|
||||
*
|
||||
* <p>Note that there are potential security implications in failing to set an array
|
||||
* of allowed fields. In the case of HTTP form POST data for example, malicious clients
|
||||
* can attempt to subvert an application by supplying values for fields or properties
|
||||
* that do not exist on the form. In some cases this could lead to illegal data being
|
||||
* set on command objects <i>or their nested objects</i>. For this reason, it is
|
||||
* <b>highly recommended to specify the {@link #setAllowedFields allowedFields} property</b>
|
||||
* on the DataBinder.
|
||||
* <p><strong>WARNING</strong>: Data binding can lead to security issues by exposing
|
||||
* parts of the object graph that are not meant to be accessed or modified by
|
||||
* external clients. Therefore the design and use of data binding should be considered
|
||||
* carefully with regard to security. For more details, please refer to the dedicated
|
||||
* sections on data binding for
|
||||
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design">Spring Web MVC</a> and
|
||||
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-ann-initbinder-model-design">Spring WebFlux</a>
|
||||
* in the reference manual.
|
||||
*
|
||||
* <p>The binding results can be examined via the {@link BindingResult} interface,
|
||||
* extending the {@link Errors} interface: see the {@link #getBindingResult()} method.
|
||||
@@ -96,6 +98,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Rob Harrop
|
||||
* @author Stephane Nicoll
|
||||
* @author Kazuki Shimizu
|
||||
* @author Sam Brannen
|
||||
* @see #setAllowedFields
|
||||
* @see #setRequiredFields
|
||||
* @see #registerCustomEditor
|
||||
@@ -415,13 +418,21 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register fields that should be allowed for binding. Default is all
|
||||
* fields. Restrict this for example to avoid unwanted modifications
|
||||
* by malicious users when binding HTTP request parameters.
|
||||
* <p>Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching
|
||||
* can be implemented by overriding the {@code isAllowed} method.
|
||||
* <p>Alternatively, specify a list of <i>disallowed</i> fields.
|
||||
* @param allowedFields array of field names
|
||||
* Register field patterns that should be allowed for binding.
|
||||
* <p>Default is all fields.
|
||||
* <p>Restrict this for example to avoid unwanted modifications by malicious
|
||||
* users when binding HTTP request parameters.
|
||||
* <p>Supports {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
|
||||
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
|
||||
* well as direct equality.
|
||||
* <p>The default implementation of this method stores allowed field patterns
|
||||
* in {@linkplain PropertyAccessorUtils#canonicalPropertyName(String) canonical}
|
||||
* form. Subclasses which override this method must therefore take this into
|
||||
* account.
|
||||
* <p>More sophisticated matching can be implemented by overriding the
|
||||
* {@link #isAllowed} method.
|
||||
* <p>Alternatively, specify a list of <i>disallowed</i> field patterns.
|
||||
* @param allowedFields array of allowed field patterns
|
||||
* @see #setDisallowedFields
|
||||
* @see #isAllowed(String)
|
||||
*/
|
||||
@@ -430,8 +441,9 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fields that should be allowed for binding.
|
||||
* @return array of field names
|
||||
* Return the field patterns that should be allowed for binding.
|
||||
* @return array of allowed field patterns
|
||||
* @see #setAllowedFields(String...)
|
||||
*/
|
||||
@Nullable
|
||||
public String[] getAllowedFields() {
|
||||
@@ -439,23 +451,44 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register fields that should <i>not</i> be allowed for binding. Default is none.
|
||||
* Mark fields as disallowed for example to avoid unwanted modifications
|
||||
* by malicious users when binding HTTP request parameters.
|
||||
* <p>Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching
|
||||
* can be implemented by overriding the {@code isAllowed} method.
|
||||
* <p>Alternatively, specify a list of <i>allowed</i> fields.
|
||||
* @param disallowedFields array of field names
|
||||
* Register field patterns that should <i>not</i> be allowed for binding.
|
||||
* <p>Default is none.
|
||||
* <p>Mark fields as disallowed, for example to avoid unwanted
|
||||
* modifications by malicious users when binding HTTP request parameters.
|
||||
* <p>Supports {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
|
||||
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
|
||||
* well as direct equality.
|
||||
* <p>The default implementation of this method stores disallowed field patterns
|
||||
* in {@linkplain PropertyAccessorUtils#canonicalPropertyName(String) canonical}
|
||||
* form. As of Spring Framework 5.2.21, the default implementation also transforms
|
||||
* disallowed field patterns to {@linkplain String#toLowerCase() lowercase} to
|
||||
* support case-insensitive pattern matching in {@link #isAllowed}. Subclasses
|
||||
* which override this method must therefore take both of these transformations
|
||||
* into account.
|
||||
* <p>More sophisticated matching can be implemented by overriding the
|
||||
* {@link #isAllowed} method.
|
||||
* <p>Alternatively, specify a list of <i>allowed</i> field patterns.
|
||||
* @param disallowedFields array of disallowed field patterns
|
||||
* @see #setAllowedFields
|
||||
* @see #isAllowed(String)
|
||||
*/
|
||||
public void setDisallowedFields(@Nullable String... disallowedFields) {
|
||||
this.disallowedFields = PropertyAccessorUtils.canonicalPropertyNames(disallowedFields);
|
||||
if (disallowedFields == null) {
|
||||
this.disallowedFields = null;
|
||||
}
|
||||
else {
|
||||
String[] fieldPatterns = new String[disallowedFields.length];
|
||||
for (int i = 0; i < fieldPatterns.length; i++) {
|
||||
fieldPatterns[i] = PropertyAccessorUtils.canonicalPropertyName(disallowedFields[i]).toLowerCase();
|
||||
}
|
||||
this.disallowedFields = fieldPatterns;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fields that should <i>not</i> be allowed for binding.
|
||||
* @return array of field names
|
||||
* Return the field patterns that should <i>not</i> be allowed for binding.
|
||||
* @return array of disallowed field patterns
|
||||
* @see #setDisallowedFields(String...)
|
||||
*/
|
||||
@Nullable
|
||||
public String[] getDisallowedFields() {
|
||||
@@ -767,15 +800,20 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the given field is allowed for binding.
|
||||
* Invoked for each passed-in property value.
|
||||
* <p>The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches,
|
||||
* as well as direct equality, in the specified lists of allowed fields and
|
||||
* disallowed fields. A field matching a disallowed pattern will not be accepted
|
||||
* even if it also happens to match a pattern in the allowed list.
|
||||
* <p>Can be overridden in subclasses.
|
||||
* Determine if the given field is allowed for binding.
|
||||
* <p>Invoked for each passed-in property value.
|
||||
* <p>Checks for {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
|
||||
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
|
||||
* well as direct equality, in the configured lists of allowed field patterns
|
||||
* and disallowed field patterns.
|
||||
* <p>Matching against allowed field patterns is case-sensitive; whereas,
|
||||
* matching against disallowed field patterns is case-insensitive.
|
||||
* <p>A field matching a disallowed pattern will not be accepted even if it
|
||||
* also happens to match a pattern in the allowed list.
|
||||
* <p>Can be overridden in subclasses, but care must be taken to honor the
|
||||
* aforementioned contract.
|
||||
* @param field the field to check
|
||||
* @return if the field is allowed
|
||||
* @return {@code true} if the field is allowed
|
||||
* @see #setAllowedFields
|
||||
* @see #setDisallowedFields
|
||||
* @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String)
|
||||
@@ -784,7 +822,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
String[] allowed = getAllowedFields();
|
||||
String[] disallowed = getDisallowedFields();
|
||||
return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) &&
|
||||
(ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field)));
|
||||
(ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field.toLowerCase())));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+30
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,8 +17,10 @@
|
||||
package org.springframework.validation.beanvalidation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -291,12 +293,17 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
|
||||
configureParameterNameProvider(this.parameterNameDiscoverer, configuration);
|
||||
}
|
||||
|
||||
List<InputStream> mappingStreams = null;
|
||||
if (this.mappingLocations != null) {
|
||||
mappingStreams = new ArrayList<>(this.mappingLocations.length);
|
||||
for (Resource location : this.mappingLocations) {
|
||||
try {
|
||||
configuration.addMapping(location.getInputStream());
|
||||
InputStream stream = location.getInputStream();
|
||||
mappingStreams.add(stream);
|
||||
configuration.addMapping(stream);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
closeMappingStreams(mappingStreams);
|
||||
throw new IllegalStateException("Cannot read mapping resource: " + location);
|
||||
}
|
||||
}
|
||||
@@ -307,8 +314,13 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
|
||||
// Allow for custom post-processing before we actually build the ValidatorFactory.
|
||||
postProcessConfiguration(configuration);
|
||||
|
||||
this.validatorFactory = configuration.buildValidatorFactory();
|
||||
setTargetValidator(this.validatorFactory.getValidator());
|
||||
try {
|
||||
this.validatorFactory = configuration.buildValidatorFactory();
|
||||
setTargetValidator(this.validatorFactory.getValidator());
|
||||
}
|
||||
finally {
|
||||
closeMappingStreams(mappingStreams);
|
||||
}
|
||||
}
|
||||
|
||||
private void configureParameterNameProvider(ParameterNameDiscoverer discoverer, Configuration<?> configuration) {
|
||||
@@ -329,6 +341,18 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
|
||||
});
|
||||
}
|
||||
|
||||
private void closeMappingStreams(@Nullable List<InputStream> mappingStreams){
|
||||
if (!CollectionUtils.isEmpty(mappingStreams)) {
|
||||
for (InputStream stream : mappingStreams) {
|
||||
try {
|
||||
stream.close();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process the given Bean Validation configuration,
|
||||
* adding to or overriding any of its settings.
|
||||
@@ -397,7 +421,7 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
|
||||
return super.unwrap(type);
|
||||
}
|
||||
catch (ValidationException ex) {
|
||||
// ignore - we'll try ValidatorFactory unwrapping next
|
||||
// Ignore - we'll try ValidatorFactory unwrapping next
|
||||
}
|
||||
}
|
||||
if (this.validatorFactory != null) {
|
||||
@@ -405,7 +429,7 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
|
||||
return this.validatorFactory.unwrap(type);
|
||||
}
|
||||
catch (ValidationException ex) {
|
||||
// ignore if just being asked for ValidatorFactory
|
||||
// Ignore if just being asked for ValidatorFactory
|
||||
if (ValidatorFactory.class == type) {
|
||||
return (T) this.validatorFactory;
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -179,7 +179,7 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
|
||||
}
|
||||
}
|
||||
else {
|
||||
// got no BindingResult - can only do standard rejectValue call
|
||||
// Got no BindingResult - can only do standard rejectValue call
|
||||
// with automatic extraction of the current field value
|
||||
errors.rejectValue(field, errorCode, errorArgs, violation.getMessage());
|
||||
}
|
||||
@@ -386,7 +386,7 @@ public class SpringValidatorAdapter implements SmartValidator, javax.validation.
|
||||
return (type != null ? this.targetValidator.unwrap(type) : (T) this.targetValidator);
|
||||
}
|
||||
catch (ValidationException ex) {
|
||||
// ignore if just being asked for plain Validator
|
||||
// Ignore if just being asked for plain JSR-303 Validator
|
||||
if (javax.validation.Validator.class == type) {
|
||||
return (T) this.targetValidator;
|
||||
}
|
||||
|
||||
+6
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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,7 @@ package org.springframework.validation.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.ui.ConcurrentModel;
|
||||
import org.springframework.validation.BindingResult;
|
||||
|
||||
@@ -36,17 +37,19 @@ import org.springframework.validation.BindingResult;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
* @see BindingResult
|
||||
* @see BindingAwareModelMap
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class BindingAwareConcurrentModel extends ConcurrentModel {
|
||||
|
||||
@Override
|
||||
public Object put(String key, Object value) {
|
||||
@Nullable
|
||||
public Object put(String key, @Nullable Object value) {
|
||||
removeBindingResultIfNecessary(key, value);
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
private void removeBindingResultIfNecessary(String key, Object value) {
|
||||
private void removeBindingResultIfNecessary(String key, @Nullable Object value) {
|
||||
if (!key.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
|
||||
String resultKey = BindingResult.MODEL_KEY_PREFIX + key;
|
||||
BindingResult result = (BindingResult) get(resultKey);
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2021 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,7 @@ package org.springframework.validation.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.validation.BindingResult;
|
||||
|
||||
@@ -39,7 +40,7 @@ import org.springframework.validation.BindingResult;
|
||||
public class BindingAwareModelMap extends ExtendedModelMap {
|
||||
|
||||
@Override
|
||||
public Object put(String key, Object value) {
|
||||
public Object put(String key, @Nullable Object value) {
|
||||
removeBindingResultIfNecessary(key, value);
|
||||
return super.put(key, value);
|
||||
}
|
||||
@@ -50,7 +51,7 @@ public class BindingAwareModelMap extends ExtendedModelMap {
|
||||
super.putAll(map);
|
||||
}
|
||||
|
||||
private void removeBindingResultIfNecessary(Object key, Object value) {
|
||||
private void removeBindingResultIfNecessary(Object key, @Nullable Object value) {
|
||||
if (key instanceof String) {
|
||||
String attributeName = (String) key;
|
||||
if (!attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
|
||||
|
||||
+8
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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,24 +36,26 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class AtAspectJAfterThrowingTests {
|
||||
|
||||
@Test
|
||||
public void testAccessThrowable() throws Exception {
|
||||
public void testAccessThrowable() {
|
||||
ClassPathXmlApplicationContext ctx =
|
||||
new ClassPathXmlApplicationContext(getClass().getSimpleName() + "-context.xml", getClass());
|
||||
new ClassPathXmlApplicationContext(getClass().getSimpleName() + "-context.xml", getClass());
|
||||
|
||||
ITestBean bean = (ITestBean) ctx.getBean("testBean");
|
||||
ExceptionHandlingAspect aspect = (ExceptionHandlingAspect) ctx.getBean("aspect");
|
||||
|
||||
assertThat(AopUtils.isAopProxy(bean)).isTrue();
|
||||
IOException exceptionThrown = null;
|
||||
try {
|
||||
bean.unreliableFileOperation();
|
||||
}
|
||||
catch (IOException e) {
|
||||
//
|
||||
catch (IOException ex) {
|
||||
exceptionThrown = ex;
|
||||
}
|
||||
|
||||
assertThat(aspect.handled).isEqualTo(1);
|
||||
assertThat(aspect.lastException).isNotNull();
|
||||
assertThat(aspect.lastException).isSameAs(exceptionThrown);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user