mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
315 Commits
v5.3.0-RC2
...
v5.3.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ffc44db78 | |||
| 25101fb034 | |||
| cb44ae62e9 | |||
| 01fb4dbeba | |||
| 07fadae27d | |||
| 2b77c08e09 | |||
| cb2b141d31 | |||
| 3b92d45984 | |||
| 1195b3a0b0 | |||
| 56721669d1 | |||
| e87e03c539 | |||
| 194cebd730 | |||
| 7418c4b7b7 | |||
| ad42010785 | |||
| 10f6a22315 | |||
| c970c318f4 | |||
| 834032df1f | |||
| 5efa4ad442 | |||
| 7ef3257b03 | |||
| 0172424635 | |||
| 994ec708fc | |||
| da15fe4c94 | |||
| a1d134b086 | |||
| 3c85c263d2 | |||
| 7b3f53de50 | |||
| ec7425c1f4 | |||
| ef05eb3729 | |||
| 8ac39a50fe | |||
| d82cb15439 | |||
| 8c1d06e0c4 | |||
| 01892c6524 | |||
| 9776929a9d | |||
| 4337d8465c | |||
| 8f0ad73bfd | |||
| 3714d0e401 | |||
| 227d85a6b4 | |||
| a1320cd450 | |||
| c7f2f50c15 | |||
| 396fb0cd51 | |||
| 5328184f3a | |||
| 1e19e51c9c | |||
| 69aed83504 | |||
| 0978cc629a | |||
| 0c825621b8 | |||
| e7b0b65244 | |||
| e9caee1b6e | |||
| 622d0831a3 | |||
| 56dbe06d9b | |||
| 11f6c4e7ab | |||
| d46091565b | |||
| 42216b77df | |||
| 43faa439ab | |||
| 1f701d9bad | |||
| 73e1f24ac1 | |||
| 86f9716fef | |||
| 80701082cd | |||
| 3612990344 | |||
| dea2029e94 | |||
| 77d6f8bc00 | |||
| 23006d417b | |||
| d8dafbc49d | |||
| cc4a1af091 | |||
| fd70a9e95d | |||
| 298cb22bfe | |||
| 4ed645e710 | |||
| 7c47f554c0 | |||
| 642d47ef8d | |||
| 42e492ef2a | |||
| 4fb5d59c64 | |||
| e6324bd578 | |||
| 45a9227047 | |||
| f1af8a6ae9 | |||
| c4d7e6ff46 | |||
| 922d5d271a | |||
| 05e3f271b6 | |||
| a7cce64e5d | |||
| a0544e78ea | |||
| c92dccea1b | |||
| 9ec96f6141 | |||
| 135682e073 | |||
| 6a0377b1a2 | |||
| 8c3a05bbcf | |||
| 684e695b08 | |||
| 8130bf505f | |||
| 9beca06404 | |||
| ae75db2657 | |||
| 4cc831238c | |||
| 41835ba5a4 | |||
| de0b5bc5a1 | |||
| b29723623b | |||
| 2ee231dee2 | |||
| b56dbd2aa8 | |||
| c9b27af64f | |||
| 9139cb85bd | |||
| 367fbbb981 | |||
| 1b1ba47912 | |||
| 041bff4e56 | |||
| bc5a10c70d | |||
| 3d31750acc | |||
| c22a483c3d | |||
| 238354a081 | |||
| 51b1306d70 | |||
| 7206a23d33 | |||
| 83996b12cc | |||
| 0fd6c100a6 | |||
| 0819a9fcc9 | |||
| 0b580d194d | |||
| 500b54b86f | |||
| 942400ae47 | |||
| b92d249f45 | |||
| 204a7fe91f | |||
| 42d3bc47c9 | |||
| 94fcb37d30 | |||
| ba9325446c | |||
| 5338d8b5e9 | |||
| 1b0be5862d | |||
| 48af36c6fa | |||
| bd4e915abf | |||
| c419ea7ba7 | |||
| 66292cd7a1 | |||
| 6eec1acdac | |||
| bc32d513d9 | |||
| 3851b291da | |||
| 2b1f229998 | |||
| fc5e3c335f | |||
| daf9a82e8c | |||
| b019f30a15 | |||
| d5b5e894c9 | |||
| 2899652d8e | |||
| 560a504a83 | |||
| 79f79e9306 | |||
| bd2640a9d6 | |||
| 6825287360 | |||
| 737d77a739 | |||
| 1f13516528 | |||
| 6bb3ad793e | |||
| c73cff8bad | |||
| e592634290 | |||
| b25637e8ad | |||
| 0c347769a2 | |||
| 2497d4285f | |||
| 32238cc996 | |||
| d80dd50c9d | |||
| bdf120f1ef | |||
| e781d21097 | |||
| 8cef13cc30 | |||
| 2214ff8ce0 | |||
| 411539ffef | |||
| 11aa901634 | |||
| 6e51370490 | |||
| bcd2b9a8a7 | |||
| 17dd7785f6 | |||
| 21f9d05bd9 | |||
| 19911af30a | |||
| 6acb091c4e | |||
| 99ed01e3f7 | |||
| 2657757566 | |||
| e1f51b4bf8 | |||
| f2f84bfa7a | |||
| 6d67bdf04f | |||
| 97c8628bd6 | |||
| 990a9c74b9 | |||
| b815accca9 | |||
| 4e8d6bea4d | |||
| 079ca80854 | |||
| 412aa06d86 | |||
| d5b3e65718 | |||
| a4679b87b9 | |||
| fbf8e37f94 | |||
| 8c3cab7ead | |||
| 0015fd6734 | |||
| b077e4cd85 | |||
| 0a0299693b | |||
| 41bdde5d6b | |||
| 2e2d662158 | |||
| 68934f1b79 | |||
| f781c459b4 | |||
| 5628c8cd63 | |||
| 0f6038af70 | |||
| 3eb2c5e22f | |||
| 3f722a9c7f | |||
| acbbf61be8 | |||
| 154f0c71c9 | |||
| 7be7e5beb4 | |||
| 37504e75e9 | |||
| b89b37729c | |||
| 141c79bc8f | |||
| 885a5048c1 | |||
| e981bf34b3 | |||
| 44239b2d7b | |||
| a00c483927 | |||
| 96ffa14141 | |||
| 9f632cf0c1 | |||
| a9dec6a4af | |||
| 9c2fd0bd05 | |||
| 6e936a4081 | |||
| b2d337c527 | |||
| 24cef14683 | |||
| bb4e802af5 | |||
| b01adadf60 | |||
| 9c11887fe8 | |||
| 4503260282 | |||
| d91b66a04c | |||
| d1e6883d5d | |||
| 5170f3f594 | |||
| a4d546a9d6 | |||
| fc9650a9a6 | |||
| 82fa3f3fc1 | |||
| 1fb33fbd3c | |||
| b6dae6acba | |||
| a3cd8e11b0 | |||
| f50c2caf59 | |||
| 0aa3205e38 | |||
| 5b910a87c3 | |||
| 9ebd3ee1fe | |||
| 3f01af6f7c | |||
| f329748657 | |||
| d12996a6a8 | |||
| d04c5f8b2c | |||
| 41247d49ba | |||
| 16c8676e5b | |||
| d7f1813cd7 | |||
| ada255d584 | |||
| 7aef0c78a0 | |||
| 188a5a086b | |||
| 705cf09ad7 | |||
| 449377908f | |||
| b1fef925af | |||
| 9287c15583 | |||
| 9a4a593c7f | |||
| c840ba9989 | |||
| 3924947773 | |||
| 9d70d2023c | |||
| 82835b99ec | |||
| a3f3a136c4 | |||
| a2ff03074f | |||
| a3fcfc5d2a | |||
| 1247e4a0e8 | |||
| 1676470729 | |||
| 4726663f81 | |||
| 86af93a504 | |||
| efe933ad83 | |||
| 5429c7afeb | |||
| 73eefeac5f | |||
| 1a50732daa | |||
| 1c82ba425b | |||
| 946e5c9bbf | |||
| af4d68b1a5 | |||
| a40cc8bbe0 | |||
| 6e640c806f | |||
| e66e34766e | |||
| 5644a7aebb | |||
| 7af726480f | |||
| 51fb49be34 | |||
| c524849774 | |||
| f2eb67373a | |||
| cf988f81b5 | |||
| 417e7e03d4 | |||
| 8d86d61f9f | |||
| fb4363e4e0 | |||
| 1ec6843913 | |||
| cff57ca1fe | |||
| 784d72cc56 | |||
| 4480269432 | |||
| 9a1323aad5 | |||
| eec6ec8f44 | |||
| 6946fe2f74 | |||
| db9e0b0ccb | |||
| aaa2d21058 | |||
| b8b854db8c | |||
| 95110d8257 | |||
| cfc3522641 | |||
| 621295dbd8 | |||
| 07769ddcc6 | |||
| c748870853 | |||
| f7ec92c647 | |||
| bd91dc70d5 | |||
| 7bee3d1574 | |||
| dff9924a0c | |||
| bfb2ce6e2a | |||
| 76eb5e6e2c | |||
| 43f595f80e | |||
| 6f131ba1c4 | |||
| d18fbab7a8 | |||
| a4d0af802a | |||
| db3d537e72 | |||
| a0727191e1 | |||
| 702a05e926 | |||
| 0df6d60fea | |||
| 5ab1097b43 | |||
| d77ecb26a9 | |||
| 6d4b286b1a | |||
| 970afbc614 | |||
| f13c43bafe | |||
| 23211c784e | |||
| a2ff139edd | |||
| 424e28bc35 | |||
| 4952336419 | |||
| babcbd0d2b | |||
| c45b106d95 | |||
| b7d1bbb991 | |||
| 603a1dd319 | |||
| 6b978cb5dc | |||
| b79548d12e | |||
| f9e11593a6 | |||
| 57ac32b808 | |||
| 2eee56d0e2 | |||
| a271a0a2bf | |||
| 69af56c4e9 | |||
| a924677b49 | |||
| 7e647ab1d8 | |||
| abd79d43af | |||
| 431ec906c4 | |||
| 1d96f6a266 | |||
| e5ae2cb0fe |
+32
-32
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'io.spring.dependency-management' version '1.0.9.RELEASE' apply false
|
||||
id 'io.spring.nohttp' version '0.0.5.RELEASE'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.4.10' apply false
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.4.21' apply false
|
||||
id 'org.jetbrains.dokka' version '0.10.1' apply false
|
||||
id 'org.asciidoctor.jvm.convert' version '3.1.0'
|
||||
id 'org.asciidoctor.jvm.pdf' version '3.1.0'
|
||||
@@ -9,7 +9,7 @@ plugins {
|
||||
id "io.freefair.aspectj" version '5.1.1' apply false
|
||||
id "com.github.ben-manes.versions" version '0.28.0'
|
||||
id "me.champeau.gradle.jmh" version "0.5.0" apply false
|
||||
id "org.jetbrains.kotlin.plugin.serialization" version "1.4.10" apply false
|
||||
id "org.jetbrains.kotlin.plugin.serialization" version "1.4.21" apply false
|
||||
}
|
||||
|
||||
ext {
|
||||
@@ -25,22 +25,22 @@ configure(allprojects) { project ->
|
||||
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom "com.fasterxml.jackson:jackson-bom:2.11.3"
|
||||
mavenBom "io.netty:netty-bom:4.1.52.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.0-RC2"
|
||||
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR7"
|
||||
mavenBom "io.rsocket:rsocket-bom:1.1.0-RC1"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.32.v20200930"
|
||||
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.4.10"
|
||||
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.4.0-M1"
|
||||
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.0"
|
||||
mavenBom "io.netty:netty-bom:4.1.54.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.2"
|
||||
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR8"
|
||||
mavenBom "io.rsocket:rsocket-bom:1.1.0"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.35.v20201120"
|
||||
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.4.21"
|
||||
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.4.2"
|
||||
mavenBom "org.junit:junit-bom:5.7.0"
|
||||
}
|
||||
dependencies {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.13.3') {
|
||||
dependencySet(group: 'org.apache.logging.log4j', version: '2.14.0') {
|
||||
entry 'log4j-api'
|
||||
entry 'log4j-core'
|
||||
entry 'log4j-slf4j-impl'
|
||||
entry 'log4j-jul'
|
||||
entry 'log4j-slf4j-impl'
|
||||
}
|
||||
dependency "org.slf4j:slf4j-api:1.7.30"
|
||||
dependency("com.google.code.findbugs:findbugs:3.0.1") {
|
||||
@@ -53,7 +53,7 @@ configure(allprojects) { project ->
|
||||
entry 'aspectjtools'
|
||||
entry 'aspectjweaver'
|
||||
}
|
||||
dependencySet(group: 'org.codehaus.groovy', version: '3.0.6') {
|
||||
dependencySet(group: 'org.codehaus.groovy', version: '3.0.7') {
|
||||
entry 'groovy'
|
||||
entry 'groovy-jsr223'
|
||||
entry 'groovy-templates' // requires findbugs for warning-free compilation
|
||||
@@ -64,18 +64,18 @@ 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.reactivex.rxjava3:rxjava:3.0.7"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.2.RELEASE"
|
||||
dependency "io.reactivex.rxjava3:rxjava:3.0.8"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.4.RELEASE"
|
||||
|
||||
dependency "com.caucho:hessian:4.0.63"
|
||||
dependency "com.fasterxml:aalto-xml:1.2.2"
|
||||
dependency("com.fasterxml.woodstox:woodstox-core:6.2.1") {
|
||||
dependency("com.fasterxml.woodstox:woodstox-core:6.2.3") {
|
||||
exclude group: "stax", name: "stax-api"
|
||||
}
|
||||
dependency "com.google.code.gson:gson:2.8.6"
|
||||
dependency "com.google.protobuf:protobuf-java-util:3.13.0"
|
||||
dependency "com.google.protobuf:protobuf-java-util:3.14.0"
|
||||
dependency "com.googlecode.protobuf-java-format:protobuf-java-format:1.4"
|
||||
dependency("com.thoughtworks.xstream:xstream:1.4.13") {
|
||||
dependency("com.thoughtworks.xstream:xstream:1.4.14") {
|
||||
exclude group: "xpp3", name: "xpp3_min"
|
||||
exclude group: "xmlpull", name: "xmlpull"
|
||||
}
|
||||
@@ -88,7 +88,7 @@ configure(allprojects) { project ->
|
||||
entry 'jibx-run'
|
||||
}
|
||||
dependency "org.ogce:xpp3:1.1.6"
|
||||
dependency "org.yaml:snakeyaml:1.26"
|
||||
dependency "org.yaml:snakeyaml:1.27"
|
||||
dependencySet(group: 'org.jetbrains.kotlinx', version: '1.0.0') {
|
||||
entry 'kotlinx-serialization-core'
|
||||
entry 'kotlinx-serialization-json'
|
||||
@@ -96,7 +96,7 @@ configure(allprojects) { project ->
|
||||
|
||||
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.librepdf:openpdf:1.3.23"
|
||||
dependency "com.rometools:rome:1.15.0"
|
||||
dependency "commons-io:commons-io:2.5"
|
||||
dependency "io.vavr:vavr:0.10.3"
|
||||
@@ -123,23 +123,23 @@ 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-core:5.4.25.Final"
|
||||
dependency "org.hibernate:hibernate-validator:6.1.6.Final"
|
||||
dependency "org.webjars:webjars-locator-core:0.46"
|
||||
dependency "org.webjars:underscorejs:1.8.3"
|
||||
|
||||
dependencySet(group: 'org.apache.tomcat', version: '9.0.38') {
|
||||
dependencySet(group: 'org.apache.tomcat', version: '9.0.40') {
|
||||
entry 'tomcat-util'
|
||||
entry('tomcat-websocket') {
|
||||
exclude group: "org.apache.tomcat", name: "tomcat-websocket-api"
|
||||
exclude group: "org.apache.tomcat", name: "tomcat-servlet-api"
|
||||
}
|
||||
}
|
||||
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.38') {
|
||||
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.40') {
|
||||
entry 'tomcat-embed-core'
|
||||
entry 'tomcat-embed-websocket'
|
||||
}
|
||||
dependencySet(group: 'io.undertow', version: '2.2.2.Final') {
|
||||
dependencySet(group: 'io.undertow', version: '2.2.3.Final') {
|
||||
entry 'undertow-core'
|
||||
entry('undertow-websockets-jsr') {
|
||||
exclude group: "org.jboss.spec.javax.websocket", name: "jboss-websocket-api_1.1_spec"
|
||||
@@ -154,7 +154,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") {
|
||||
@@ -190,25 +190,25 @@ configure(allprojects) { project ->
|
||||
dependency "org.testng:testng:7.3.0"
|
||||
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') {
|
||||
exclude group: "org.hamcrest", name: "hamcrest-core"
|
||||
}
|
||||
}
|
||||
dependencySet(group: 'org.mockito', version: '3.5.13') {
|
||||
dependencySet(group: 'org.mockito', version: '3.6.28') {
|
||||
entry('mockito-core') {
|
||||
exclude group: "org.hamcrest", name: "hamcrest-core"
|
||||
}
|
||||
entry 'mockito-junit-jupiter'
|
||||
}
|
||||
dependency "io.mockk:mockk:1.10.0"
|
||||
dependency "io.mockk:mockk:1.10.2"
|
||||
|
||||
dependency("net.sourceforge.htmlunit:htmlunit:2.44.0") {
|
||||
dependency("net.sourceforge.htmlunit:htmlunit:2.45.0") {
|
||||
exclude group: "commons-logging", name: "commons-logging"
|
||||
}
|
||||
dependency("org.seleniumhq.selenium:htmlunit-driver:2.44.0") {
|
||||
dependency("org.seleniumhq.selenium:htmlunit-driver:2.45.0") {
|
||||
exclude group: "commons-logging", name: "commons-logging"
|
||||
}
|
||||
dependency("org.seleniumhq.selenium:selenium-java:3.141.59") {
|
||||
@@ -291,7 +291,6 @@ configure(allprojects) { project ->
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url "https://repo.spring.io/libs-spring-framework-build" }
|
||||
maven { url "https://repo.spring.io/milestone" } // Reactor
|
||||
}
|
||||
}
|
||||
configurations.all {
|
||||
@@ -340,7 +339,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "8.36.2"
|
||||
toolVersion = "8.38"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
@@ -380,6 +379,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
"https://fasterxml.github.io/jackson-databind/javadoc/2.10/",
|
||||
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.10/",
|
||||
"https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/",
|
||||
"https://projectreactor.io/docs/test/release/api/",
|
||||
"https://junit.org/junit4/javadoc/4.13.1/",
|
||||
"https://junit.org/junit5/docs/5.7.0/api/"
|
||||
] as String[]
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
changelog:
|
||||
repository: spring-projects/spring-framework
|
||||
sections:
|
||||
- title: ":star: New Features"
|
||||
labels:
|
||||
- "type: enhancement"
|
||||
- title: ":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,9 @@
|
||||
logging:
|
||||
level:
|
||||
io.spring.concourse: DEBUG
|
||||
distribute:
|
||||
optional-deployments:
|
||||
- '.*\\.zip'
|
||||
spring:
|
||||
main:
|
||||
banner-mode: off
|
||||
+1
-1
@@ -11,7 +11,7 @@ rm -rf /var/lib/apt/lists/*
|
||||
|
||||
curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.3/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.2.0/concourse-release-scripts-0.2.0.jar
|
||||
curl --output /opt/concourse-release-scripts.jar https://repo.spring.io/release/io/spring/concourse/releasescripts/concourse-release-scripts/0.2.1/concourse-release-scripts-0.2.1.jar
|
||||
|
||||
###########################################################
|
||||
# JAVA
|
||||
|
||||
+162
-83
@@ -1,28 +1,61 @@
|
||||
anchors:
|
||||
artifactory-task-params: &artifactory-task-params
|
||||
ARTIFACTORY_SERVER: ((artifactory-server))
|
||||
ARTIFACTORY_USERNAME: ((artifactory-username))
|
||||
ARTIFACTORY_PASSWORD: ((artifactory-password))
|
||||
bintray-task-params: &bintray-task-params
|
||||
BINTRAY_SUBJECT: ((bintray-subject))
|
||||
BINTRAY_REPO: ((bintray-repo))
|
||||
BINTRAY_USERNAME: ((bintray-username))
|
||||
BINTRAY_API_KEY: ((bintray-api-key))
|
||||
docker-resource-source: &docker-resource-source
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
tag: 5.3.x
|
||||
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))
|
||||
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
|
||||
sonatype-task-params: &sonatype-task-params
|
||||
SONATYPE_USER_TOKEN: ((sonatype-user-token))
|
||||
SONATYPE_PASSWORD_TOKEN: ((sonatype-user-token-password))
|
||||
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: artifactory-resource
|
||||
type: docker-image
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/artifactory-resource
|
||||
tag: 0.0.12-SNAPSHOT
|
||||
tag: 0.0.12
|
||||
- name: github-status-resource
|
||||
type: docker-image
|
||||
type: registry-image
|
||||
source:
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
- name: email
|
||||
type: docker-image
|
||||
source:
|
||||
repository: pcfseceng/email-resource
|
||||
tag: 1.0.19
|
||||
- name: slack-notification
|
||||
type: docker-image
|
||||
type: registry-image
|
||||
source:
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
|
||||
resources:
|
||||
- name: git-repo
|
||||
type: git
|
||||
icon: github-circle
|
||||
icon: github
|
||||
source:
|
||||
uri: ((github-repo))
|
||||
username: ((github-username))
|
||||
@@ -37,7 +70,7 @@ resources:
|
||||
location: Europe/Vienna
|
||||
- name: ci-images-git-repo
|
||||
type: git
|
||||
icon: github-circle
|
||||
icon: github
|
||||
source:
|
||||
uri: ((github-repo))
|
||||
branch: ((branch))
|
||||
@@ -46,34 +79,26 @@ resources:
|
||||
type: docker-image
|
||||
icon: docker
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-ci-image
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
tag: 5.3.x
|
||||
- name: spring-framework-jdk11-ci-image
|
||||
type: docker-image
|
||||
icon: docker
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-jdk11-ci-image
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
tag: 5.3.x
|
||||
- name: spring-framework-jdk14-ci-image
|
||||
type: docker-image
|
||||
icon: docker
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-jdk14-ci-image
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
tag: 5.3.x
|
||||
- name: spring-framework-jdk15-ci-image
|
||||
type: docker-image
|
||||
icon: docker
|
||||
source:
|
||||
<<: *docker-resource-source
|
||||
repository: ((docker-hub-organization))/spring-framework-jdk15-ci-image
|
||||
username: ((docker-hub-username))
|
||||
password: ((docker-hub-password))
|
||||
tag: 5.3.x
|
||||
- name: artifactory-repo
|
||||
type: artifactory-resource
|
||||
icon: package-variant
|
||||
@@ -119,6 +144,24 @@ resources:
|
||||
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-spring-framework-ci-images
|
||||
plan:
|
||||
@@ -158,23 +201,14 @@ jobs:
|
||||
file: git-repo/ci/tasks/build-project.yml
|
||||
params:
|
||||
BRANCH: ((branch))
|
||||
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))
|
||||
<<: *gradle-enterprise-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
text: >
|
||||
:concourse-failed: ${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!
|
||||
[<https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|build log>]
|
||||
[<$TEXT_FILE_CONTENT|build scan>]
|
||||
text_file: git-repo/build/build-scan-uri.txt
|
||||
silent: true
|
||||
icon_emoji: ":concourse:"
|
||||
username: concourse-ci
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- put: artifactory-repo
|
||||
@@ -224,23 +258,14 @@ jobs:
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
params:
|
||||
BRANCH: ((branch))
|
||||
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))
|
||||
<<: *gradle-enterprise-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk11-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
text: >
|
||||
:concourse-failed: ${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!
|
||||
[<https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|build log>]
|
||||
[<$TEXT_FILE_CONTENT|build scan>]
|
||||
text_file: git-repo/build/build-scan-uri.txt
|
||||
silent: true
|
||||
icon_emoji: ":concourse:"
|
||||
username: concourse-ci
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk11-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: jdk14-build
|
||||
@@ -261,23 +286,14 @@ jobs:
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
params:
|
||||
BRANCH: ((branch))
|
||||
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))
|
||||
<<: *gradle-enterprise-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk14-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
text: >
|
||||
:concourse-failed: ${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!
|
||||
[<https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|build log>]
|
||||
[<$TEXT_FILE_CONTENT|build scan>]
|
||||
text_file: git-repo/build/build-scan-uri.txt
|
||||
silent: true
|
||||
icon_emoji: ":concourse:"
|
||||
username: concourse-ci
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk14-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: jdk15-build
|
||||
@@ -298,23 +314,14 @@ jobs:
|
||||
file: git-repo/ci/tasks/check-project.yml
|
||||
params:
|
||||
BRANCH: ((branch))
|
||||
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))
|
||||
<<: *gradle-enterprise-task-params
|
||||
on_failure:
|
||||
do:
|
||||
- put: repo-status-jdk15-build
|
||||
params: { state: "failure", commit: "git-repo" }
|
||||
- put: slack-alert
|
||||
params:
|
||||
text: >
|
||||
:concourse-failed: ${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!
|
||||
[<https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|build log>]
|
||||
[<$TEXT_FILE_CONTENT|build scan>]
|
||||
text_file: git-repo/build/build-scan-uri.txt
|
||||
silent: true
|
||||
icon_emoji: ":concourse:"
|
||||
username: concourse-ci
|
||||
<<: *slack-fail-params
|
||||
- put: repo-status-jdk15-build
|
||||
params: { state: "success", commit: "git-repo" }
|
||||
- name: stage-milestone
|
||||
@@ -328,9 +335,7 @@ jobs:
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
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))
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
@@ -355,9 +360,15 @@ jobs:
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: M
|
||||
ARTIFACTORY_SERVER: ((artifactory-server))
|
||||
ARTIFACTORY_USERNAME: ((artifactory-username))
|
||||
ARTIFACTORY_PASSWORD: ((artifactory-password))
|
||||
<<: *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:
|
||||
@@ -369,9 +380,7 @@ jobs:
|
||||
file: git-repo/ci/tasks/stage-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
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))
|
||||
<<: *gradle-enterprise-task-params
|
||||
- put: artifactory-repo
|
||||
params:
|
||||
<<: *artifactory-params
|
||||
@@ -396,13 +405,83 @@ jobs:
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RC
|
||||
ARTIFACTORY_SERVER: ((artifactory-server))
|
||||
ARTIFACTORY_USERNAME: ((artifactory-username))
|
||||
ARTIFACTORY_PASSWORD: ((artifactory-password))
|
||||
<<: *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: spring-framework-ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- task: stage
|
||||
image: spring-framework-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: spring-framework-ci-image
|
||||
- get: git-repo
|
||||
trigger: false
|
||||
- get: artifactory-repo
|
||||
trigger: false
|
||||
passed: [stage-release]
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: promote
|
||||
image: spring-framework-ci-image
|
||||
file: git-repo/ci/tasks/promote-version.yml
|
||||
params:
|
||||
RELEASE_TYPE: RELEASE
|
||||
<<: *artifactory-task-params
|
||||
<<: *bintray-task-params
|
||||
- name: sync-to-maven-central
|
||||
serial: true
|
||||
plan:
|
||||
- get: spring-framework-ci-image
|
||||
- get: git-repo
|
||||
- get: artifactory-repo
|
||||
trigger: true
|
||||
passed: [promote-release]
|
||||
params:
|
||||
download_artifacts: false
|
||||
save_build_info: true
|
||||
- task: sync-to-maven-central
|
||||
image: spring-framework-ci-image
|
||||
file: git-repo/ci/tasks/sync-to-maven-central.yml
|
||||
params:
|
||||
<<: *bintray-task-params
|
||||
<<: *sonatype-task-params
|
||||
- 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: "Build"
|
||||
- name: "builds"
|
||||
jobs: ["build", "jdk11-build", "jdk14-build", "jdk15-build"]
|
||||
- name: "Release"
|
||||
jobs: ["stage-milestone","stage-rc", "promote-milestone","promote-rc"]
|
||||
- name: "CI Images"
|
||||
- name: "releases"
|
||||
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone","promote-rc", "promote-release", "sync-to-maven-central"]
|
||||
- name: "ci-images"
|
||||
jobs: ["build-spring-framework-ci-images"]
|
||||
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
CONFIG_DIR=git-repo/ci/config
|
||||
version=$( cat version/version )
|
||||
|
||||
java -jar /github-changelog-generator.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/changelog-generator.yml \
|
||||
${version} generated-changelog/changelog.md
|
||||
|
||||
echo ${version} > generated-changelog/version
|
||||
echo v${version} > generated-changelog/tag
|
||||
@@ -1,13 +1,16 @@
|
||||
#!/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 promote $RELEASE_TYPE $BUILD_INFO_LOCATION > /dev/null || { exit 1; }
|
||||
java -jar /opt/concourse-release-scripts.jar promote $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
|
||||
|
||||
java -jar /opt/concourse-release-scripts.jar distribute $RELEASE_TYPE $BUILD_INFO_LOCATION > /dev/null || { exit 1; }
|
||||
java -jar /opt/concourse-release-scripts.jar \
|
||||
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
|
||||
distribute $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
|
||||
|
||||
echo "Promotion complete"
|
||||
echo $version > version/version
|
||||
|
||||
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
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
---
|
||||
platform: linux
|
||||
image_resource:
|
||||
type: docker-image
|
||||
source:
|
||||
repository: springio/github-changelog-generator
|
||||
tag: '0.0.4'
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: version
|
||||
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,16 @@
|
||||
---
|
||||
platform: linux
|
||||
inputs:
|
||||
- name: git-repo
|
||||
- name: artifactory-repo
|
||||
outputs:
|
||||
- name: version
|
||||
params:
|
||||
BINTRAY_REPO:
|
||||
BINTRAY_SUBJECT:
|
||||
BINTRAY_USERNAME:
|
||||
BINTRAY_API_KEY:
|
||||
SONATYPE_USER_TOKEN:
|
||||
SONATYPE_PASSWORD_TOKEN:
|
||||
run:
|
||||
path: git-repo/ci/scripts/sync-to-maven-central.sh
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.3.0-SNAPSHOT
|
||||
version=5.3.2
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
+5
-2
@@ -3,7 +3,7 @@ configurations {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.4.3.RELEASE")
|
||||
asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.5.0")
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -106,11 +106,14 @@ dokka {
|
||||
externalDocumentationLink {
|
||||
url = new URL("https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/")
|
||||
}
|
||||
externalDocumentationLink {
|
||||
url = new URL("https://r2dbc.io/spec/0.8.3.RELEASE/api/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task downloadResources(type: Download) {
|
||||
def version = "0.2.2.RELEASE"
|
||||
def version = "0.2.5"
|
||||
src "https://repo.spring.io/release/io/spring/docresources/" +
|
||||
"spring-doc-resources/$version/spring-doc-resources-${version}.zip"
|
||||
dest project.file("$buildDir/docs/spring-doc-resources.zip")
|
||||
|
||||
+18
-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
|
||||
}
|
||||
}
|
||||
@@ -56,6 +55,23 @@ eclipse.classpath.file.whenMerged { classpath ->
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that test fixture dependencies are handled properly in Gradle 6.7.
|
||||
// Bug fixed in Gradle 6.8: https://github.com/gradle/gradle/issues/14932
|
||||
eclipse.classpath.file.whenMerged {
|
||||
entries.findAll { it instanceof ProjectDependency }.each {
|
||||
it.entryAttributes.remove('without_test_code')
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that JMH sources and resources are treated as test classpath entries
|
||||
// so that they can see test fixtures.
|
||||
// https://github.com/melix/jmh-gradle-plugin/issues/157
|
||||
eclipse.classpath.file.whenMerged {
|
||||
entries.findAll { it.path =~ /src\/jmh\/(java|resources)/ }.each {
|
||||
it.entryAttributes['test'] = 'true'
|
||||
}
|
||||
}
|
||||
|
||||
// Allow projects to be used as WTP modules
|
||||
eclipse.project.natures "org.eclipse.wst.common.project.facet.core.nature"
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
+3
-13
@@ -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.
|
||||
@@ -350,17 +350,8 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
|
||||
return this.discoveredThrowingType;
|
||||
}
|
||||
|
||||
private boolean isVariableName(String name) {
|
||||
char[] chars = name.toCharArray();
|
||||
if (!Character.isJavaIdentifierStart(chars[0])) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < chars.length; i++) {
|
||||
if (!Character.isJavaIdentifierPart(chars[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
private static boolean isVariableName(String name) {
|
||||
return AspectJProxyUtils.isVariableName(name);
|
||||
}
|
||||
|
||||
|
||||
@@ -640,7 +631,6 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
|
||||
}
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
|
||||
// TODO AopUtils.invokeJoinpointUsingReflection
|
||||
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
|
||||
+2
-14
@@ -470,22 +470,10 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
|
||||
*/
|
||||
@Nullable
|
||||
private String maybeExtractVariableName(@Nullable String candidateToken) {
|
||||
if (!StringUtils.hasLength(candidateToken)) {
|
||||
return null;
|
||||
}
|
||||
if (Character.isJavaIdentifierStart(candidateToken.charAt(0)) &&
|
||||
Character.isLowerCase(candidateToken.charAt(0))) {
|
||||
char[] tokenChars = candidateToken.toCharArray();
|
||||
for (char tokenChar : tokenChars) {
|
||||
if (!Character.isJavaIdentifierPart(tokenChar)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (AspectJProxyUtils.isVariableName(candidateToken)) {
|
||||
return candidateToken;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
@@ -21,6 +21,8 @@ import java.util.List;
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.PointcutAdvisor;
|
||||
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility methods for working with AspectJ proxies.
|
||||
@@ -73,4 +75,19 @@ public abstract class AspectJProxyUtils {
|
||||
((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
|
||||
}
|
||||
|
||||
static boolean isVariableName(@Nullable String name) {
|
||||
if (!StringUtils.hasLength(name)) {
|
||||
return false;
|
||||
}
|
||||
if (!Character.isJavaIdentifierStart(name.charAt(0))) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < name.length(); i++) {
|
||||
if (!Character.isJavaIdentifierPart(name.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-3
@@ -221,10 +221,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
-16
@@ -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.
|
||||
@@ -160,23 +160,12 @@ public class AspectJProxyFactory extends ProxyCreatorSupport {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singleton aspect instance for the supplied aspect type. An instance
|
||||
* is created if one cannot be found in the instance cache.
|
||||
* Get the singleton aspect instance for the supplied aspect type.
|
||||
* An instance is created if one cannot be found in the instance cache.
|
||||
*/
|
||||
private Object getSingletonAspectInstance(Class<?> aspectClass) {
|
||||
// Quick check without a lock...
|
||||
Object instance = aspectCache.get(aspectClass);
|
||||
if (instance == null) {
|
||||
synchronized (aspectCache) {
|
||||
// To be safe, check within full lock now...
|
||||
instance = aspectCache.get(aspectClass);
|
||||
if (instance == null) {
|
||||
instance = new SimpleAspectInstanceFactory(aspectClass).getAspectInstance();
|
||||
aspectCache.put(aspectClass, instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
return aspectCache.computeIfAbsent(aspectClass,
|
||||
clazz -> new SimpleAspectInstanceFactory(clazz).getAspectInstance());
|
||||
}
|
||||
|
||||
|
||||
|
||||
+10
-10
@@ -51,6 +51,7 @@ import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.ConvertingComparator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.comparator.InstanceComparator;
|
||||
|
||||
@@ -70,7 +71,11 @@ import org.springframework.util.comparator.InstanceComparator;
|
||||
@SuppressWarnings("serial")
|
||||
public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable {
|
||||
|
||||
private static final Comparator<Method> METHOD_COMPARATOR;
|
||||
// Exclude @Pointcut methods
|
||||
private static final MethodFilter adviceMethodFilter = ReflectionUtils.USER_DECLARED_METHODS
|
||||
.and(method -> (AnnotationUtils.getAnnotation(method, Pointcut.class) == null));
|
||||
|
||||
private static final Comparator<Method> adviceMethodComparator;
|
||||
|
||||
static {
|
||||
// Note: although @After is ordered before @AfterReturning and @AfterThrowing,
|
||||
@@ -86,7 +91,7 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
|
||||
return (ann != null ? ann.getAnnotation() : null);
|
||||
});
|
||||
Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
|
||||
METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
|
||||
adviceMethodComparator = adviceKindComparator.thenComparing(methodNameComparator);
|
||||
}
|
||||
|
||||
|
||||
@@ -160,15 +165,10 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
|
||||
}
|
||||
|
||||
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
|
||||
final List<Method> methods = new ArrayList<>();
|
||||
ReflectionUtils.doWithMethods(aspectClass, method -> {
|
||||
// Exclude pointcuts
|
||||
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
|
||||
methods.add(method);
|
||||
}
|
||||
}, ReflectionUtils.USER_DECLARED_METHODS);
|
||||
List<Method> methods = new ArrayList<>();
|
||||
ReflectionUtils.doWithMethods(aspectClass, methods::add, adviceMethodFilter);
|
||||
if (methods.size() > 1) {
|
||||
methods.sort(METHOD_COMPARATOR);
|
||||
methods.sort(adviceMethodComparator);
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -114,6 +114,15 @@ public interface Advised extends TargetClassAware {
|
||||
*/
|
||||
Advisor[] getAdvisors();
|
||||
|
||||
/**
|
||||
* Return the number of advisors applying to this proxy.
|
||||
* <p>The default implementation delegates to {@code getAdvisors().length}.
|
||||
* @since 5.3.1
|
||||
*/
|
||||
default int getAdvisorCount() {
|
||||
return getAdvisors().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an advisor at the end of the advisor chain.
|
||||
* <p>The Advisor may be an {@link org.springframework.aop.IntroductionAdvisor},
|
||||
|
||||
@@ -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.
|
||||
@@ -95,12 +95,6 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
*/
|
||||
private List<Advisor> advisors = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Array updated on changes to the advisors list, which is easier
|
||||
* to manipulate internally.
|
||||
*/
|
||||
private Advisor[] advisorArray = new Advisor[0];
|
||||
|
||||
|
||||
/**
|
||||
* No-arg constructor for use as a JavaBean.
|
||||
@@ -244,7 +238,12 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
|
||||
@Override
|
||||
public final Advisor[] getAdvisors() {
|
||||
return this.advisorArray;
|
||||
return this.advisors.toArray(new Advisor[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAdvisorCount() {
|
||||
return this.advisors.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -292,7 +291,6 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
}
|
||||
}
|
||||
|
||||
updateAdvisorArray();
|
||||
adviceChanged();
|
||||
}
|
||||
|
||||
@@ -339,7 +337,6 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
Assert.notNull(advisor, "Advisor must not be null");
|
||||
this.advisors.add(advisor);
|
||||
}
|
||||
updateAdvisorArray();
|
||||
adviceChanged();
|
||||
}
|
||||
}
|
||||
@@ -363,27 +360,18 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
"Illegal position " + pos + " in advisor list with size " + this.advisors.size());
|
||||
}
|
||||
this.advisors.add(pos, advisor);
|
||||
updateAdvisorArray();
|
||||
adviceChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bring the array up to date with the list.
|
||||
*/
|
||||
protected final void updateAdvisorArray() {
|
||||
this.advisorArray = this.advisors.toArray(new Advisor[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows uncontrolled access to the {@link List} of {@link Advisor Advisors}.
|
||||
* <p>Use with care, and remember to {@link #updateAdvisorArray() refresh the advisor array}
|
||||
* and {@link #adviceChanged() fire advice changed events} when making any modifications.
|
||||
* <p>Use with care, and remember to {@link #adviceChanged() fire advice changed events}
|
||||
* when making any modifications.
|
||||
*/
|
||||
protected final List<Advisor> getAdvisorsInternal() {
|
||||
return this.advisors;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addAdvice(Advice advice) throws AopConfigException {
|
||||
int pos = this.advisors.size();
|
||||
@@ -521,7 +509,6 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
Assert.notNull(advisor, "Advisor must not be null");
|
||||
this.advisors.add(advisor);
|
||||
}
|
||||
updateAdvisorArray();
|
||||
adviceChanged();
|
||||
}
|
||||
|
||||
@@ -536,7 +523,6 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
copy.advisorChainFactory = this.advisorChainFactory;
|
||||
copy.interfaces = this.interfaces;
|
||||
copy.advisors = this.advisors;
|
||||
copy.updateAdvisorArray();
|
||||
return copy;
|
||||
}
|
||||
|
||||
@@ -553,7 +539,6 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
|
||||
this.methodCache = new ConcurrentHashMap<>(32);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toProxyConfigString() {
|
||||
return toString();
|
||||
|
||||
@@ -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.
|
||||
@@ -206,7 +206,7 @@ public abstract class AopProxyUtils {
|
||||
* Check equality of the advisors behind the given AdvisedSupport objects.
|
||||
*/
|
||||
public static boolean equalsAdvisors(AdvisedSupport a, AdvisedSupport b) {
|
||||
return Arrays.equals(a.getAdvisors(), b.getAdvisors());
|
||||
return a.getAdvisorCount() == b.getAdvisorCount() && Arrays.equals(a.getAdvisors(), b.getAdvisors());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
*/
|
||||
public CglibAopProxy(AdvisedSupport config) throws AopConfigException {
|
||||
Assert.notNull(config, "AdvisedSupport must not be null");
|
||||
if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
|
||||
if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
|
||||
throw new AopConfigException("No advisors and no TargetSource specified");
|
||||
}
|
||||
this.advised = config;
|
||||
@@ -942,11 +942,11 @@ class CglibAopProxy implements AopProxy, Serializable {
|
||||
}
|
||||
// Advice instance identity is unimportant to the proxy class:
|
||||
// All that matters is type and ordering.
|
||||
Advisor[] thisAdvisors = this.advised.getAdvisors();
|
||||
Advisor[] thatAdvisors = otherAdvised.getAdvisors();
|
||||
if (thisAdvisors.length != thatAdvisors.length) {
|
||||
if (this.advised.getAdvisorCount() != otherAdvised.getAdvisorCount()) {
|
||||
return false;
|
||||
}
|
||||
Advisor[] thisAdvisors = this.advised.getAdvisors();
|
||||
Advisor[] thatAdvisors = otherAdvised.getAdvisors();
|
||||
for (int i = 0; i < thisAdvisors.length; i++) {
|
||||
Advisor thisAdvisor = thisAdvisors[i];
|
||||
Advisor thatAdvisor = thatAdvisors[i];
|
||||
|
||||
@@ -104,7 +104,7 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
|
||||
*/
|
||||
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
|
||||
Assert.notNull(config, "AdvisedSupport must not be null");
|
||||
if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
|
||||
if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
|
||||
throw new AopConfigException("No advisors and no TargetSource specified");
|
||||
}
|
||||
this.advised = config;
|
||||
|
||||
+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")
|
||||
|
||||
@@ -25,6 +25,7 @@ sourceSets {
|
||||
compileGroovy {
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
options.compilerArgs += "-Werror"
|
||||
}
|
||||
|
||||
// This module also builds Kotlin code and the compileKotlin task naturally depends on
|
||||
|
||||
+6
-3
@@ -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.
|
||||
@@ -422,9 +422,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;
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
+4
-5
@@ -644,21 +644,20 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
Object cachedFieldValue = null;
|
||||
if (value != null || this.required) {
|
||||
this.cachedFieldValue = desc;
|
||||
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(
|
||||
cachedFieldValue = new ShortcutDependencyDescriptor(
|
||||
desc, autowiredBeanName, field.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.cachedFieldValue = null;
|
||||
}
|
||||
this.cachedFieldValue = cachedFieldValue;
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
|
||||
+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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-3
@@ -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,7 +40,6 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link AutowireCandidateResolver} implementation that matches bean definition qualifiers
|
||||
@@ -188,7 +187,7 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
||||
foundMeta = true;
|
||||
// Only accept fallback match if @Qualifier annotation has a value...
|
||||
// Otherwise it is just a marker for a custom qualifier annotation.
|
||||
if ((fallbackToMeta && StringUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) ||
|
||||
if ((fallbackToMeta && ObjectUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) ||
|
||||
!checkQualifier(bdHolder, metaAnn, typeConverter)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+2
-4
@@ -1288,10 +1288,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* @param registry the PropertyEditorRegistry to initialize
|
||||
*/
|
||||
protected void registerCustomEditors(PropertyEditorRegistry registry) {
|
||||
PropertyEditorRegistrySupport registrySupport =
|
||||
(registry instanceof PropertyEditorRegistrySupport ? (PropertyEditorRegistrySupport) registry : null);
|
||||
if (registrySupport != null) {
|
||||
registrySupport.useConfigValueEditors();
|
||||
if (registry instanceof PropertyEditorRegistrySupport) {
|
||||
((PropertyEditorRegistrySupport) registry).useConfigValueEditors();
|
||||
}
|
||||
if (!this.propertyEditorRegistrars.isEmpty()) {
|
||||
for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
|
||||
|
||||
+7
-5
@@ -67,6 +67,7 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Delegate for resolving constructors and factory methods.
|
||||
*
|
||||
* <p>Performs constructor resolution through argument matching.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
@@ -85,7 +86,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();
|
||||
@@ -149,7 +150,7 @@ class ConstructorResolver {
|
||||
}
|
||||
}
|
||||
if (argsToResolve != null) {
|
||||
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
|
||||
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,6 +411,7 @@ class ConstructorResolver {
|
||||
if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
|
||||
throw new ImplicitlyAppearedSingletonException();
|
||||
}
|
||||
this.beanFactory.registerDependentBean(factoryBeanName, beanName);
|
||||
factoryClass = factoryBean.getClass();
|
||||
isStatic = false;
|
||||
}
|
||||
@@ -444,7 +446,7 @@ class ConstructorResolver {
|
||||
}
|
||||
}
|
||||
if (argsToResolve != null) {
|
||||
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);
|
||||
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -816,7 +818,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);
|
||||
@@ -829,7 +831,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);
|
||||
|
||||
+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
-26
@@ -43,8 +43,6 @@ import java.util.stream.IntStream;
|
||||
import javax.annotation.Priority;
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
@@ -121,8 +119,6 @@ import static org.mockito.Mockito.verify;
|
||||
*/
|
||||
class DefaultListableBeanFactoryTests {
|
||||
|
||||
private static final Log factoryLog = LogFactory.getLog(DefaultListableBeanFactory.class);
|
||||
|
||||
private DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
|
||||
|
||||
|
||||
@@ -789,12 +785,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
|
||||
@@ -1842,8 +1839,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
|
||||
@@ -2392,8 +2388,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
|
||||
@@ -2407,8 +2402,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
|
||||
@@ -2423,8 +2417,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
|
||||
@@ -2546,14 +2539,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);
|
||||
@@ -2563,8 +2557,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
|
||||
@@ -2600,15 +2593,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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.
|
||||
@@ -126,7 +126,7 @@ public class DefinitionMetadataEqualsHashCodeTests {
|
||||
assertThat(equal.hashCode()).as("Hash code for equal instances should match").isEqualTo(master.hashCode());
|
||||
|
||||
assertThat(notEqual).as("Should not be equal").isNotEqualTo(master);
|
||||
assertThat(notEqual.hashCode()).as("Hash code for non-equal instances should not match").isNotEqualTo((long) master.hashCode());
|
||||
assertThat(notEqual.hashCode()).as("Hash code for non-equal instances should not match").isNotEqualTo(master.hashCode());
|
||||
|
||||
assertThat(subclass).as("Subclass should be equal").isEqualTo(master);
|
||||
assertThat(subclass.hashCode()).as("Hash code for subclass should match").isEqualTo(master.hashCode());
|
||||
|
||||
+1
-2
@@ -155,8 +155,7 @@ public abstract class AbstractCacheManager implements CacheManager, Initializing
|
||||
* @param name the name of the cache to be added
|
||||
*/
|
||||
private void updateCacheNames(String name) {
|
||||
Set<String> cacheNames = new LinkedHashSet<>(this.cacheNames.size() + 1);
|
||||
cacheNames.addAll(this.cacheNames);
|
||||
Set<String> cacheNames = new LinkedHashSet<>(this.cacheNames);
|
||||
cacheNames.add(name);
|
||||
this.cacheNames = Collections.unmodifiableSet(cacheNames);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.context;
|
||||
|
||||
import java.util.EventListener;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by application event listeners.
|
||||
@@ -45,4 +46,17 @@ public interface ApplicationListener<E extends ApplicationEvent> extends EventLi
|
||||
*/
|
||||
void onApplicationEvent(E event);
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code ApplicationListener} for the given payload consumer.
|
||||
* @param consumer the event payload consumer
|
||||
* @param <T> the type of the event payload
|
||||
* @return a corresponding {@code ApplicationListener} instance
|
||||
* @since 5.3
|
||||
* @see PayloadApplicationEvent
|
||||
*/
|
||||
static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
|
||||
return event -> consumer.accept(event.getPayload());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-6
@@ -99,7 +99,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
* @see #setBeanNameGenerator
|
||||
*/
|
||||
public static final AnnotationBeanNameGenerator IMPORT_BEAN_NAME_GENERATOR =
|
||||
new FullyQualifiedAnnotationBeanNameGenerator();
|
||||
FullyQualifiedAnnotationBeanNameGenerator.INSTANCE;
|
||||
|
||||
private static final String IMPORT_REGISTRY_BEAN_NAME =
|
||||
ConfigurationClassPostProcessor.class.getName() + ".importRegistry";
|
||||
@@ -427,15 +427,11 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
|
||||
}
|
||||
}
|
||||
if (configBeanDefs.isEmpty()) {
|
||||
if (configBeanDefs.isEmpty() || IN_NATIVE_IMAGE) {
|
||||
// nothing to enhance -> return immediately
|
||||
enhanceConfigClasses.end();
|
||||
return;
|
||||
}
|
||||
if (IN_NATIVE_IMAGE) {
|
||||
throw new BeanDefinitionStoreException("@Configuration classes need to be marked as " +
|
||||
"proxyBeanMethods=false. Found: " + configBeanDefs.keySet());
|
||||
}
|
||||
|
||||
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
|
||||
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
|
||||
|
||||
+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();
|
||||
|
||||
+16
-1
@@ -37,6 +37,7 @@ import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.PayloadApplicationEvent;
|
||||
import org.springframework.context.expression.AnnotatedElementKey;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
@@ -95,6 +96,12 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
private EventExpressionEvaluator evaluator;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new ApplicationListenerMethodAdapter.
|
||||
* @param beanName the name of the bean to invoke the listener method on
|
||||
* @param targetClass the target class that the method is declared on
|
||||
* @param method the listener method to invoke
|
||||
*/
|
||||
public ApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method) {
|
||||
this.beanName = beanName;
|
||||
this.method = BridgeMethodResolver.findBridgedMethod(method);
|
||||
@@ -135,7 +142,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
|
||||
private static int resolveOrder(Method method) {
|
||||
Order ann = AnnotatedElementUtils.findMergedAnnotation(method, Order.class);
|
||||
return (ann != null ? ann.value() : 0);
|
||||
return (ann != null ? ann.value() : Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
|
||||
@@ -332,6 +339,14 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||
return this.applicationContext.getBean(this.beanName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the target listener method.
|
||||
* @since 5.3
|
||||
*/
|
||||
protected Method getTargetMethod() {
|
||||
return this.targetMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the condition to use.
|
||||
* <p>Matches the {@code condition} attribute of the {@link EventListener}
|
||||
|
||||
+2
-1
@@ -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.
|
||||
@@ -29,6 +29,7 @@ import org.springframework.core.Ordered;
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.2
|
||||
* @see ApplicationListenerMethodAdapter
|
||||
*/
|
||||
public class DefaultEventListenerFactory implements EventListenerFactory, Ordered {
|
||||
|
||||
|
||||
-31
@@ -25,8 +25,6 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.metrics.ApplicationStartup;
|
||||
import org.springframework.core.metrics.StartupStep;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
|
||||
@@ -57,9 +55,6 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
|
||||
@Nullable
|
||||
private ErrorHandler errorHandler;
|
||||
|
||||
@Nullable
|
||||
private ApplicationStartup applicationStartup;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SimpleApplicationEventMulticaster.
|
||||
@@ -127,22 +122,6 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
|
||||
return this.errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ApplicationStartup} to track event listener invocations during startup.
|
||||
* @since 5.3
|
||||
*/
|
||||
public void setApplicationStartup(@Nullable ApplicationStartup applicationStartup) {
|
||||
this.applicationStartup = applicationStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current application startup for this multicaster.
|
||||
*/
|
||||
@Nullable
|
||||
public ApplicationStartup getApplicationStartup() {
|
||||
return this.applicationStartup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void multicastEvent(ApplicationEvent event) {
|
||||
multicastEvent(event, resolveDefaultEventType(event));
|
||||
@@ -156,16 +135,6 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
|
||||
if (executor != null) {
|
||||
executor.execute(() -> invokeListener(listener, event));
|
||||
}
|
||||
else if (this.applicationStartup != null) {
|
||||
StartupStep invocationStep = this.applicationStartup.start("spring.event.invoke-listener");
|
||||
invokeListener(listener, event);
|
||||
invocationStep.tag("event", event::toString);
|
||||
if (eventType != null) {
|
||||
invocationStep.tag("eventType", eventType::toString);
|
||||
}
|
||||
invocationStep.tag("listener", listener::toString);
|
||||
invocationStep.end();
|
||||
}
|
||||
else {
|
||||
invokeListener(listener, event);
|
||||
}
|
||||
|
||||
+1
-3
@@ -815,9 +815,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
}
|
||||
else {
|
||||
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
|
||||
simpleApplicationEventMulticaster.setApplicationStartup(getApplicationStartup());
|
||||
this.applicationEventMulticaster = simpleApplicationEventMulticaster;
|
||||
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
|
||||
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
|
||||
|
||||
+1
-2
@@ -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.
|
||||
@@ -106,7 +106,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,
|
||||
|
||||
|
||||
+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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -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,6 +36,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class AtAspectJAnnotationBindingTests {
|
||||
|
||||
private AnnotatedTestBean testBean;
|
||||
|
||||
private ClassPathXmlApplicationContext ctx;
|
||||
|
||||
|
||||
@@ -70,8 +71,7 @@ public class AtAspectJAnnotationBindingTests {
|
||||
class AtAspectJAnnotationBindingTestAspect {
|
||||
|
||||
@Around("execution(* *(..)) && @annotation(testAnn)")
|
||||
public Object doWithAnnotation(ProceedingJoinPoint pjp, TestAnnotation testAnn)
|
||||
throws Throwable {
|
||||
public Object doWithAnnotation(ProceedingJoinPoint pjp, TestAnnotation testAnn) throws Throwable {
|
||||
String annValue = testAnn.value();
|
||||
Object result = pjp.proceed();
|
||||
return (result instanceof String ? annValue + " " + result : result);
|
||||
|
||||
+101
-99
@@ -20,7 +20,6 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -66,7 +65,6 @@ import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
@@ -77,13 +75,13 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
public class ConfigurationClassPostProcessorTests {
|
||||
class ConfigurationClassPostProcessorTests {
|
||||
|
||||
private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
void setup() {
|
||||
QualifierAnnotationAutowireCandidateResolver acr = new QualifierAnnotationAutowireCandidateResolver();
|
||||
acr.setBeanFactory(this.beanFactory);
|
||||
this.beanFactory.setAutowireCandidateResolver(acr);
|
||||
@@ -99,7 +97,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
* We test for such a case below, and in doing so prove that enhancement is working.
|
||||
*/
|
||||
@Test
|
||||
public void enhancementIsPresentBecauseSingletonSemanticsAreRespected() {
|
||||
void enhancementIsPresentBecauseSingletonSemanticsAreRespected() {
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
@@ -107,11 +105,13 @@ public class ConfigurationClassPostProcessorTests {
|
||||
Foo foo = beanFactory.getBean("foo", Foo.class);
|
||||
Bar bar = beanFactory.getBean("bar", Bar.class);
|
||||
assertThat(bar.foo).isSameAs(foo);
|
||||
assertThat(Arrays.asList(beanFactory.getDependentBeans("foo")).contains("bar")).isTrue();
|
||||
assertThat(beanFactory.getDependentBeans("foo")).contains("bar");
|
||||
assertThat(beanFactory.getDependentBeans("config")).contains("foo");
|
||||
assertThat(beanFactory.getDependentBeans("config")).contains("bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enhancementIsPresentBecauseSingletonSemanticsAreRespectedUsingAsm() {
|
||||
void enhancementIsPresentBecauseSingletonSemanticsAreRespectedUsingAsm() {
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class.getName()));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
@@ -119,11 +119,13 @@ public class ConfigurationClassPostProcessorTests {
|
||||
Foo foo = beanFactory.getBean("foo", Foo.class);
|
||||
Bar bar = beanFactory.getBean("bar", Bar.class);
|
||||
assertThat(bar.foo).isSameAs(foo);
|
||||
assertThat(Arrays.asList(beanFactory.getDependentBeans("foo")).contains("bar")).isTrue();
|
||||
assertThat(beanFactory.getDependentBeans("foo")).contains("bar");
|
||||
assertThat(beanFactory.getDependentBeans("config")).contains("foo");
|
||||
assertThat(beanFactory.getDependentBeans("config")).contains("bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enhancementIsNotPresentForProxyBeanMethodsFlagSetToFalse() {
|
||||
void enhancementIsNotPresentForProxyBeanMethodsFlagSetToFalse() {
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(NonEnhancedSingletonBeanConfig.class));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
@@ -134,7 +136,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enhancementIsNotPresentForProxyBeanMethodsFlagSetToFalseUsingAsm() {
|
||||
void enhancementIsNotPresentForProxyBeanMethodsFlagSetToFalseUsingAsm() {
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(NonEnhancedSingletonBeanConfig.class.getName()));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
@@ -145,7 +147,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enhancementIsNotPresentForStaticMethods() {
|
||||
void enhancementIsNotPresentForStaticMethods() {
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(StaticSingletonBeanConfig.class));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
@@ -158,7 +160,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enhancementIsNotPresentForStaticMethodsUsingAsm() {
|
||||
void enhancementIsNotPresentForStaticMethodsUsingAsm() {
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(StaticSingletonBeanConfig.class.getName()));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
@@ -171,7 +173,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configurationIntrospectionOfInnerClassesWorksWithDotNameSyntax() {
|
||||
void configurationIntrospectionOfInnerClassesWorksWithDotNameSyntax() {
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(getClass().getName() + ".SingletonBeanConfig"));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
@@ -185,7 +187,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
* if a bean class is already loaded.
|
||||
*/
|
||||
@Test
|
||||
public void alreadyLoadedConfigurationClasses() {
|
||||
void alreadyLoadedConfigurationClasses() {
|
||||
beanFactory.registerBeanDefinition("unloadedConfig", new RootBeanDefinition(UnloadedConfig.class.getName()));
|
||||
beanFactory.registerBeanDefinition("loadedConfig", new RootBeanDefinition(LoadedConfig.class));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
@@ -198,7 +200,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
* Tests whether a bean definition without a specified bean class is handled correctly.
|
||||
*/
|
||||
@Test
|
||||
public void postProcessorIntrospectsInheritedDefinitionsCorrectly() {
|
||||
void postProcessorIntrospectsInheritedDefinitionsCorrectly() {
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class));
|
||||
beanFactory.registerBeanDefinition("parent", new RootBeanDefinition(TestBean.class));
|
||||
beanFactory.registerBeanDefinition("child", new ChildBeanDefinition("parent"));
|
||||
@@ -210,96 +212,96 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithComposedConfigurationUsingReflection() {
|
||||
void postProcessorWorksWithComposedConfigurationUsingReflection() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(ComposedConfigurationClass.class);
|
||||
assertSupportForComposedAnnotation(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithComposedConfigurationUsingAsm() {
|
||||
void postProcessorWorksWithComposedConfigurationUsingAsm() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(ComposedConfigurationClass.class.getName());
|
||||
assertSupportForComposedAnnotation(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForBasePackageUsingReflection() {
|
||||
void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForBasePackageUsingReflection() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
ComposedConfigurationWithAttributeOverrideForBasePackage.class);
|
||||
assertSupportForComposedAnnotation(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForBasePackageUsingAsm() {
|
||||
void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForBasePackageUsingAsm() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
ComposedConfigurationWithAttributeOverrideForBasePackage.class.getName());
|
||||
assertSupportForComposedAnnotation(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForExcludeFilterUsingReflection() {
|
||||
void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForExcludeFilterUsingReflection() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
ComposedConfigurationWithAttributeOverrideForExcludeFilter.class);
|
||||
assertSupportForComposedAnnotationWithExclude(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForExcludeFilterUsingAsm() {
|
||||
void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForExcludeFilterUsingAsm() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
ComposedConfigurationWithAttributeOverrideForExcludeFilter.class.getName());
|
||||
assertSupportForComposedAnnotationWithExclude(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithExtendedConfigurationWithAttributeOverrideForExcludesFilterUsingReflection() {
|
||||
void postProcessorWorksWithExtendedConfigurationWithAttributeOverrideForExcludesFilterUsingReflection() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
ExtendedConfigurationWithAttributeOverrideForExcludeFilter.class);
|
||||
assertSupportForComposedAnnotationWithExclude(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithExtendedConfigurationWithAttributeOverrideForExcludesFilterUsingAsm() {
|
||||
void postProcessorWorksWithExtendedConfigurationWithAttributeOverrideForExcludesFilterUsingAsm() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
ExtendedConfigurationWithAttributeOverrideForExcludeFilter.class.getName());
|
||||
assertSupportForComposedAnnotationWithExclude(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithComposedComposedConfigurationWithAttributeOverridesUsingReflection() {
|
||||
void postProcessorWorksWithComposedComposedConfigurationWithAttributeOverridesUsingReflection() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
ComposedComposedConfigurationWithAttributeOverridesClass.class);
|
||||
assertSupportForComposedAnnotation(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithComposedComposedConfigurationWithAttributeOverridesUsingAsm() {
|
||||
void postProcessorWorksWithComposedComposedConfigurationWithAttributeOverridesUsingAsm() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
ComposedComposedConfigurationWithAttributeOverridesClass.class.getName());
|
||||
assertSupportForComposedAnnotation(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithMetaComponentScanConfigurationWithAttributeOverridesUsingReflection() {
|
||||
void postProcessorWorksWithMetaComponentScanConfigurationWithAttributeOverridesUsingReflection() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
MetaComponentScanConfigurationWithAttributeOverridesClass.class);
|
||||
assertSupportForComposedAnnotation(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithMetaComponentScanConfigurationWithAttributeOverridesUsingAsm() {
|
||||
void postProcessorWorksWithMetaComponentScanConfigurationWithAttributeOverridesUsingAsm() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
MetaComponentScanConfigurationWithAttributeOverridesClass.class.getName());
|
||||
assertSupportForComposedAnnotation(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithMetaComponentScanConfigurationWithAttributeOverridesSubclassUsingReflection() {
|
||||
void postProcessorWorksWithMetaComponentScanConfigurationWithAttributeOverridesSubclassUsingReflection() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
SubMetaComponentScanConfigurationWithAttributeOverridesClass.class);
|
||||
assertSupportForComposedAnnotation(beanDefinition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorWorksWithMetaComponentScanConfigurationWithAttributeOverridesSubclassUsingAsm() {
|
||||
void postProcessorWorksWithMetaComponentScanConfigurationWithAttributeOverridesSubclassUsingAsm() {
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||
SubMetaComponentScanConfigurationWithAttributeOverridesClass.class.getName());
|
||||
assertSupportForComposedAnnotation(beanDefinition);
|
||||
@@ -324,7 +326,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorOverridesNonApplicationBeanDefinitions() {
|
||||
void postProcessorOverridesNonApplicationBeanDefinitions() {
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class);
|
||||
rbd.setRole(RootBeanDefinition.ROLE_SUPPORT);
|
||||
beanFactory.registerBeanDefinition("bar", rbd);
|
||||
@@ -337,7 +339,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorDoesNotOverrideRegularBeanDefinitions() {
|
||||
void postProcessorDoesNotOverrideRegularBeanDefinitions() {
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class);
|
||||
rbd.setResource(new DescriptiveResource("XML or something"));
|
||||
beanFactory.registerBeanDefinition("bar", rbd);
|
||||
@@ -349,7 +351,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorDoesNotOverrideRegularBeanDefinitionsEvenWithScopedProxy() {
|
||||
void postProcessorDoesNotOverrideRegularBeanDefinitionsEvenWithScopedProxy() {
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class);
|
||||
rbd.setResource(new DescriptiveResource("XML or something"));
|
||||
BeanDefinitionHolder proxied = ScopedProxyUtils.createScopedProxy(
|
||||
@@ -363,7 +365,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postProcessorFailsOnImplicitOverrideIfOverridingIsNotAllowed() {
|
||||
void postProcessorFailsOnImplicitOverrideIfOverridingIsNotAllowed() {
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class);
|
||||
rbd.setResource(new DescriptiveResource("XML or something"));
|
||||
beanFactory.registerBeanDefinition("bar", rbd);
|
||||
@@ -378,7 +380,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test // gh-25430
|
||||
public void detectAliasOverride() {
|
||||
void detectAliasOverride() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
|
||||
beanFactory.setAllowBeanDefinitionOverriding(false);
|
||||
@@ -390,7 +392,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configurationClassesProcessedInCorrectOrder() {
|
||||
void configurationClassesProcessedInCorrectOrder() {
|
||||
beanFactory.registerBeanDefinition("config1", new RootBeanDefinition(OverridingSingletonBeanConfig.class));
|
||||
beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(SingletonBeanConfig.class));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
@@ -404,7 +406,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configurationClassesWithValidOverridingForProgrammaticCall() {
|
||||
void configurationClassesWithValidOverridingForProgrammaticCall() {
|
||||
beanFactory.registerBeanDefinition("config1", new RootBeanDefinition(OverridingAgainSingletonBeanConfig.class));
|
||||
beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(OverridingSingletonBeanConfig.class));
|
||||
beanFactory.registerBeanDefinition("config3", new RootBeanDefinition(SingletonBeanConfig.class));
|
||||
@@ -419,7 +421,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configurationClassesWithInvalidOverridingForProgrammaticCall() {
|
||||
void configurationClassesWithInvalidOverridingForProgrammaticCall() {
|
||||
beanFactory.registerBeanDefinition("config1", new RootBeanDefinition(InvalidOverridingSingletonBeanConfig.class));
|
||||
beanFactory.registerBeanDefinition("config2", new RootBeanDefinition(OverridingSingletonBeanConfig.class));
|
||||
beanFactory.registerBeanDefinition("config3", new RootBeanDefinition(SingletonBeanConfig.class));
|
||||
@@ -435,7 +437,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test // SPR-15384
|
||||
public void nestedConfigurationClassesProcessedInCorrectOrder() {
|
||||
void nestedConfigurationClassesProcessedInCorrectOrder() {
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithOrderedNestedClasses.class));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
@@ -448,7 +450,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test // SPR-16734
|
||||
public void innerConfigurationClassesProcessedInCorrectOrder() {
|
||||
void innerConfigurationClassesProcessedInCorrectOrder() {
|
||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithOrderedInnerClasses.class));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
@@ -462,7 +464,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scopedProxyTargetMarkedAsNonAutowireCandidate() {
|
||||
void scopedProxyTargetMarkedAsNonAutowireCandidate() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -479,7 +481,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processingAllowedOnlyOncePerProcessorRegistryPair() {
|
||||
void processingAllowedOnlyOncePerProcessorRegistryPair() {
|
||||
DefaultListableBeanFactory bf1 = new DefaultListableBeanFactory();
|
||||
DefaultListableBeanFactory bf2 = new DefaultListableBeanFactory();
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
@@ -492,7 +494,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjection() {
|
||||
void genericsBasedInjection() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -509,7 +511,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithScoped() {
|
||||
void genericsBasedInjectionWithScoped() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -526,7 +528,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithScopedProxy() {
|
||||
void genericsBasedInjectionWithScopedProxy() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -546,7 +548,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithScopedProxyUsingAsm() {
|
||||
void genericsBasedInjectionWithScopedProxyUsingAsm() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -566,7 +568,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithImplTypeAtInjectionPoint() {
|
||||
void genericsBasedInjectionWithImplTypeAtInjectionPoint() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -583,7 +585,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithFactoryBean() {
|
||||
void genericsBasedInjectionWithFactoryBean() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -602,7 +604,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithRawMatch() {
|
||||
void genericsBasedInjectionWithRawMatch() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawMatchingConfiguration.class));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
@@ -611,7 +613,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithWildcardMatch() {
|
||||
void genericsBasedInjectionWithWildcardMatch() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(WildcardMatchingConfiguration.class));
|
||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||
pp.postProcessBeanFactory(beanFactory);
|
||||
@@ -620,7 +622,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithWildcardWithExtendsMatch() {
|
||||
void genericsBasedInjectionWithWildcardWithExtendsMatch() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(WildcardWithExtendsConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
|
||||
@@ -628,7 +630,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithWildcardWithGenericExtendsMatch() {
|
||||
void genericsBasedInjectionWithWildcardWithGenericExtendsMatch() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(WildcardWithGenericExtendsConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
|
||||
@@ -636,12 +638,12 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithEarlyGenericsMatching() {
|
||||
void genericsBasedInjectionWithEarlyGenericsMatching() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertThat(ObjectUtils.containsElement(beanNames, "stringRepo")).isTrue();
|
||||
assertThat(beanNames).contains("stringRepo");
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertThat(beanNames.length).isEqualTo(1);
|
||||
@@ -653,13 +655,13 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatching() {
|
||||
void genericsBasedInjectionWithLateGenericsMatching() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertThat(ObjectUtils.containsElement(beanNames, "stringRepo")).isTrue();
|
||||
assertThat(beanNames).contains("stringRepo");
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertThat(beanNames.length).isEqualTo(1);
|
||||
@@ -671,12 +673,12 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithEarlyGenericsMatchingAndRawFactoryMethod() {
|
||||
void genericsBasedInjectionWithEarlyGenericsMatchingAndRawFactoryMethod() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertThat(ObjectUtils.containsElement(beanNames, "stringRepo")).isTrue();
|
||||
assertThat(beanNames).contains("stringRepo");
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertThat(beanNames.length).isEqualTo(0);
|
||||
@@ -686,13 +688,13 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatchingAndRawFactoryMethod() {
|
||||
void genericsBasedInjectionWithLateGenericsMatchingAndRawFactoryMethod() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertThat(ObjectUtils.containsElement(beanNames, "stringRepo")).isTrue();
|
||||
assertThat(beanNames).contains("stringRepo");
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertThat(beanNames.length).isEqualTo(1);
|
||||
@@ -704,12 +706,12 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithEarlyGenericsMatchingAndRawInstance() {
|
||||
void genericsBasedInjectionWithEarlyGenericsMatchingAndRawInstance() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertThat(ObjectUtils.containsElement(beanNames, "stringRepo")).isTrue();
|
||||
assertThat(beanNames).contains("stringRepo");
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertThat(beanNames.length).isEqualTo(1);
|
||||
@@ -721,13 +723,13 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatchingAndRawInstance() {
|
||||
void genericsBasedInjectionWithLateGenericsMatchingAndRawInstance() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertThat(ObjectUtils.containsElement(beanNames, "stringRepo")).isTrue();
|
||||
assertThat(beanNames).contains("stringRepo");
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertThat(beanNames.length).isEqualTo(1);
|
||||
@@ -739,7 +741,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithEarlyGenericsMatchingOnCglibProxy() {
|
||||
void genericsBasedInjectionWithEarlyGenericsMatchingOnCglibProxy() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
@@ -749,7 +751,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertThat(ObjectUtils.containsElement(beanNames, "stringRepo")).isTrue();
|
||||
assertThat(beanNames).contains("stringRepo");
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertThat(beanNames.length).isEqualTo(1);
|
||||
@@ -763,7 +765,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxy() {
|
||||
void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxy() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
@@ -774,7 +776,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertThat(ObjectUtils.containsElement(beanNames, "stringRepo")).isTrue();
|
||||
assertThat(beanNames).contains("stringRepo");
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertThat(beanNames.length).isEqualTo(1);
|
||||
@@ -788,7 +790,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFactoryMethod() {
|
||||
void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFactoryMethod() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
@@ -799,7 +801,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertThat(ObjectUtils.containsElement(beanNames, "stringRepo")).isTrue();
|
||||
assertThat(beanNames).contains("stringRepo");
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertThat(beanNames.length).isEqualTo(1);
|
||||
@@ -813,7 +815,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawInstance() {
|
||||
void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawInstance() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
@@ -824,7 +826,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||
assertThat(ObjectUtils.containsElement(beanNames, "stringRepo")).isTrue();
|
||||
assertThat(beanNames).contains("stringRepo");
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||
assertThat(beanNames.length).isEqualTo(1);
|
||||
@@ -838,7 +840,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithEarlyGenericsMatchingOnJdkProxy() {
|
||||
void genericsBasedInjectionWithEarlyGenericsMatchingOnJdkProxy() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
@@ -847,7 +849,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(RepositoryInterface.class);
|
||||
assertThat(ObjectUtils.containsElement(beanNames, "stringRepo")).isTrue();
|
||||
assertThat(beanNames).contains("stringRepo");
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
|
||||
assertThat(beanNames.length).isEqualTo(1);
|
||||
@@ -861,7 +863,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxy() {
|
||||
void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxy() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
@@ -871,7 +873,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(RepositoryInterface.class);
|
||||
assertThat(ObjectUtils.containsElement(beanNames, "stringRepo")).isTrue();
|
||||
assertThat(beanNames).contains("stringRepo");
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
|
||||
assertThat(beanNames.length).isEqualTo(1);
|
||||
@@ -885,7 +887,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactoryMethod() {
|
||||
void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactoryMethod() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
@@ -895,7 +897,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(RepositoryInterface.class);
|
||||
assertThat(ObjectUtils.containsElement(beanNames, "stringRepo")).isTrue();
|
||||
assertThat(beanNames).contains("stringRepo");
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
|
||||
assertThat(beanNames.length).isEqualTo(1);
|
||||
@@ -909,7 +911,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawInstance() {
|
||||
void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawInstance() {
|
||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
|
||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
||||
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
@@ -919,7 +921,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
beanFactory.preInstantiateSingletons();
|
||||
|
||||
String[] beanNames = beanFactory.getBeanNamesForType(RepositoryInterface.class);
|
||||
assertThat(ObjectUtils.containsElement(beanNames, "stringRepo")).isTrue();
|
||||
assertThat(beanNames).contains("stringRepo");
|
||||
|
||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
|
||||
assertThat(beanNames.length).isEqualTo(1);
|
||||
@@ -933,7 +935,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelfReferenceExclusionForFactoryMethodOnSameBean() {
|
||||
void testSelfReferenceExclusionForFactoryMethodOnSameBean() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -947,7 +949,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigWithDefaultMethods() {
|
||||
void testConfigWithDefaultMethods() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -961,7 +963,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigWithDefaultMethodsUsingAsm() {
|
||||
void testConfigWithDefaultMethodsUsingAsm() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -975,7 +977,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCircularDependency() {
|
||||
void testCircularDependency() {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(beanFactory);
|
||||
beanFactory.addBeanPostProcessor(bpp);
|
||||
@@ -988,38 +990,38 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCircularDependencyWithApplicationContext() {
|
||||
void testCircularDependencyWithApplicationContext() {
|
||||
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
|
||||
new AnnotationConfigApplicationContext(A.class, AStrich.class))
|
||||
.withMessageContaining("Circular reference");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrototypeArgumentThroughBeanMethodCall() {
|
||||
void testPrototypeArgumentThroughBeanMethodCall() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanArgumentConfigWithPrototype.class);
|
||||
ctx.getBean(FooFactory.class).createFoo(new BarArgument());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingletonArgumentThroughBeanMethodCall() {
|
||||
void testSingletonArgumentThroughBeanMethodCall() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanArgumentConfigWithSingleton.class);
|
||||
ctx.getBean(FooFactory.class).createFoo(new BarArgument());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullArgumentThroughBeanMethodCall() {
|
||||
void testNullArgumentThroughBeanMethodCall() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanArgumentConfigWithNull.class);
|
||||
ctx.getBean("aFoo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInjectionPointMatchForNarrowTargetReturnType() {
|
||||
void testInjectionPointMatchForNarrowTargetReturnType() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(FooBarConfiguration.class);
|
||||
assertThat(ctx.getBean(FooImpl.class).bar).isSameAs(ctx.getBean(BarImpl.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVarargOnBeanMethod() {
|
||||
void testVarargOnBeanMethod() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(VarargConfiguration.class, TestBean.class);
|
||||
VarargConfiguration bean = ctx.getBean(VarargConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
@@ -1028,7 +1030,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyVarargOnBeanMethod() {
|
||||
void testEmptyVarargOnBeanMethod() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(VarargConfiguration.class);
|
||||
VarargConfiguration bean = ctx.getBean(VarargConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
@@ -1036,7 +1038,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectionArgumentOnBeanMethod() {
|
||||
void testCollectionArgumentOnBeanMethod() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class, TestBean.class);
|
||||
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
@@ -1045,7 +1047,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyCollectionArgumentOnBeanMethod() {
|
||||
void testEmptyCollectionArgumentOnBeanMethod() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class);
|
||||
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
@@ -1053,7 +1055,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapArgumentOnBeanMethod() {
|
||||
void testMapArgumentOnBeanMethod() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class, DummyRunnable.class);
|
||||
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
@@ -1062,7 +1064,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyMapArgumentOnBeanMethod() {
|
||||
void testEmptyMapArgumentOnBeanMethod() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class);
|
||||
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
@@ -1070,7 +1072,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectionInjectionFromSameConfigurationClass() {
|
||||
void testCollectionInjectionFromSameConfigurationClass() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionInjectionConfiguration.class);
|
||||
CollectionInjectionConfiguration bean = ctx.getBean(CollectionInjectionConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
@@ -1079,7 +1081,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapInjectionFromSameConfigurationClass() {
|
||||
void testMapInjectionFromSameConfigurationClass() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(MapInjectionConfiguration.class);
|
||||
MapInjectionConfiguration bean = ctx.getBean(MapInjectionConfiguration.class);
|
||||
assertThat(bean.testBeans).isNotNull();
|
||||
@@ -1088,7 +1090,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBeanLookupFromSameConfigurationClass() {
|
||||
void testBeanLookupFromSameConfigurationClass() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanLookupConfiguration.class);
|
||||
BeanLookupConfiguration bean = ctx.getBean(BeanLookupConfiguration.class);
|
||||
assertThat(bean.getTestBean()).isNotNull();
|
||||
@@ -1096,7 +1098,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNameClashBetweenConfigurationClassAndBean() {
|
||||
void testNameClashBetweenConfigurationClassAndBean() {
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() -> {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyTestBean.class);
|
||||
ctx.getBean("myTestBean", TestBean.class);
|
||||
@@ -1104,7 +1106,7 @@ public class ConfigurationClassPostProcessorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBeanDefinitionRegistryPostProcessorConfig() {
|
||||
void testBeanDefinitionRegistryPostProcessorConfig() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanDefinitionRegistryPostProcessorConfig.class);
|
||||
boolean condition = ctx.getBean("myTestBean") instanceof TestBean;
|
||||
assertThat(condition).isTrue();
|
||||
|
||||
+55
@@ -307,6 +307,23 @@ public class ConfigurationClassProcessingTests {
|
||||
assertThat(tb.getLawyer()).isEqualTo(ctx.getBean(NestedTestBean.class));
|
||||
}
|
||||
|
||||
@Test // gh-26019
|
||||
public void autowiringWithDynamicPrototypeBeanClass() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
|
||||
ConfigWithDynamicPrototype.class, PrototypeDependency.class);
|
||||
|
||||
PrototypeInterface p1 = ctx.getBean(PrototypeInterface.class, 1);
|
||||
assertThat(p1).isInstanceOf(PrototypeOne.class);
|
||||
assertThat(((PrototypeOne) p1).prototypeDependency).isNotNull();
|
||||
|
||||
PrototypeInterface p2 = ctx.getBean(PrototypeInterface.class, 2);
|
||||
assertThat(p2).isInstanceOf(PrototypeTwo.class);
|
||||
|
||||
PrototypeInterface p3 = ctx.getBean(PrototypeInterface.class, 1);
|
||||
assertThat(p3).isInstanceOf(PrototypeOne.class);
|
||||
assertThat(((PrototypeOne) p3).prototypeDependency).isNotNull();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link BeanFactory}, populates it with a {@link BeanDefinition}
|
||||
@@ -632,4 +649,42 @@ public class ConfigurationClassProcessingTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class PrototypeDependency {
|
||||
}
|
||||
|
||||
interface PrototypeInterface {
|
||||
}
|
||||
|
||||
static class PrototypeOne extends AbstractPrototype {
|
||||
|
||||
@Autowired
|
||||
PrototypeDependency prototypeDependency;
|
||||
|
||||
}
|
||||
|
||||
static class PrototypeTwo extends AbstractPrototype {
|
||||
|
||||
// no autowired dependency here, in contrast to above
|
||||
}
|
||||
|
||||
static class AbstractPrototype implements PrototypeInterface {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithDynamicPrototype {
|
||||
|
||||
@Bean
|
||||
@Scope(value = "prototype")
|
||||
public PrototypeInterface getDemoBean( int i) {
|
||||
switch ( i) {
|
||||
case 1: return new PrototypeOne();
|
||||
case 2:
|
||||
default:
|
||||
return new PrototypeTwo();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -26,6 +26,7 @@ import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.PayloadApplicationEvent;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.ResolvableTypeProvider;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@@ -161,7 +162,7 @@ public class ApplicationListenerMethodAdapterTests extends AbstractApplicationEv
|
||||
Method method = ReflectionUtils.findMethod(
|
||||
SampleEvents.class, "handleGenericString", GenericTestEvent.class);
|
||||
ApplicationListenerMethodAdapter adapter = createTestInstance(method);
|
||||
assertThat(adapter.getOrder()).isEqualTo(0);
|
||||
assertThat(adapter.getOrder()).isEqualTo(Ordered.LOWEST_PRECEDENCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+34
-3
@@ -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.
|
||||
@@ -22,8 +22,11 @@ import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.PayloadApplicationEvent;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -34,14 +37,42 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
public class PayloadApplicationEventTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "rawtypes", "resource" })
|
||||
public void testEventClassWithInterface() {
|
||||
ApplicationContext ac = new AnnotationConfigApplicationContext(AuditableListener.class);
|
||||
AuditablePayloadEvent event = new AuditablePayloadEvent<>(this, "xyz");
|
||||
|
||||
AuditablePayloadEvent<String> event = new AuditablePayloadEvent<>(this, "xyz");
|
||||
ac.publishEvent(event);
|
||||
assertThat(ac.getBean(AuditableListener.class).events.contains(event)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgrammaticEventListener() {
|
||||
List<Auditable> events = new ArrayList<>();
|
||||
ApplicationListener<AuditablePayloadEvent<String>> listener = events::add;
|
||||
|
||||
ConfigurableApplicationContext ac = new GenericApplicationContext();
|
||||
ac.addApplicationListener(listener);
|
||||
ac.refresh();
|
||||
|
||||
AuditablePayloadEvent<String> event = new AuditablePayloadEvent<>(this, "xyz");
|
||||
ac.publishEvent(event);
|
||||
assertThat(events.contains(event)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgrammaticPayloadListener() {
|
||||
List<String> events = new ArrayList<>();
|
||||
ApplicationListener<PayloadApplicationEvent<String>> listener = ApplicationListener.forPayload(events::add);
|
||||
|
||||
ConfigurableApplicationContext ac = new GenericApplicationContext();
|
||||
ac.addApplicationListener(listener);
|
||||
ac.refresh();
|
||||
|
||||
AuditablePayloadEvent<String> event = new AuditablePayloadEvent<>(this, "xyz");
|
||||
ac.publishEvent(event);
|
||||
assertThat(events.contains(event.getPayload())).isTrue();
|
||||
}
|
||||
|
||||
|
||||
public interface Auditable {
|
||||
}
|
||||
|
||||
+3
-3
@@ -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.
|
||||
@@ -58,10 +58,10 @@ internal fun <T: Any> monoToDeferred(source: Mono<T>) =
|
||||
* @since 5.2
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun invokeSuspendingFunction(method: Method, bean: Any, vararg args: Any?): Publisher<*> {
|
||||
fun invokeSuspendingFunction(method: Method, target: Any, vararg args: Any?): Publisher<*> {
|
||||
val function = method.kotlinFunction!!
|
||||
val mono = mono(Dispatchers.Unconfined) {
|
||||
function.callSuspend(bean, *args.sliceArray(0..(args.size-2))).let { if (it == Unit) null else it }
|
||||
function.callSuspend(target, *args.sliceArray(0..(args.size-2))).let { if (it == Unit) null else it }
|
||||
}.onErrorMap(InvocationTargetException::class.java) { it.targetException }
|
||||
return if (function.returnType.classifier == Flow::class) {
|
||||
mono.flatMapMany { (it as Flow<Any>).asFlux() }
|
||||
|
||||
@@ -55,20 +55,20 @@ dependencies {
|
||||
optional("io.reactivex.rxjava3:rxjava")
|
||||
optional("io.netty:netty-buffer")
|
||||
testCompile("io.projectreactor:reactor-test")
|
||||
testCompile("javax.annotation:javax.annotation-api")
|
||||
testCompile("com.google.code.findbugs:jsr305")
|
||||
testCompile("org.xmlunit:xmlunit-assertj")
|
||||
testCompile("org.xmlunit:xmlunit-matchers")
|
||||
testCompile("javax.annotation:javax.annotation-api")
|
||||
testCompile("javax.xml.bind:jaxb-api")
|
||||
testCompile("com.fasterxml.woodstox:woodstox-core")
|
||||
testCompile("org.xmlunit:xmlunit-assertj")
|
||||
testCompile("org.xmlunit:xmlunit-matchers")
|
||||
testCompile(project(":kotlin-coroutines"))
|
||||
testCompile("io.projectreactor.tools:blockhound")
|
||||
testFixturesImplementation("com.google.code.findbugs:jsr305")
|
||||
testFixturesImplementation("io.projectreactor:reactor-test")
|
||||
testFixturesImplementation("org.assertj:assertj-core")
|
||||
testFixturesImplementation("com.google.code.findbugs:jsr305")
|
||||
testFixturesImplementation("org.junit.platform:junit-platform-launcher")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params")
|
||||
testFixturesImplementation("org.assertj:assertj-core")
|
||||
testFixturesImplementation("org.xmlunit:xmlunit-assertj")
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ jar {
|
||||
}
|
||||
|
||||
test {
|
||||
// make sure the classes dir is used on the test classpath (required by ResourceTests)
|
||||
// Make sure the classes dir is used on the test classpath (required by ResourceTests)
|
||||
// When test fixtures are involved, the JAR is used by default
|
||||
classpath = sourceSets.main.output.classesDirs + classpath - files(jar.archiveFile)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.core.codec;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Level;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.infra.Blackhole;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
/**
|
||||
* Benchmarks for {@link DataBufferUtils}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
@BenchmarkMode(Mode.Throughput)
|
||||
public class StringDecoderBenchmark {
|
||||
|
||||
@Benchmark
|
||||
public void parseSseLines(SseLinesState state, Blackhole blackhole) {
|
||||
blackhole.consume(state.parseLines().blockLast());
|
||||
}
|
||||
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
@SuppressWarnings({"NotNullFieldNotInitialized", "ConstantConditions"})
|
||||
public static class SseLinesState {
|
||||
|
||||
private static final Charset CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
private static final ResolvableType ELEMENT_TYPE = ResolvableType.forClass(String.class);
|
||||
|
||||
|
||||
@Param("10240")
|
||||
int totalSize;
|
||||
|
||||
@Param("2000")
|
||||
int chunkSize;
|
||||
|
||||
List<DataBuffer> chunks;
|
||||
|
||||
StringDecoder decoder = StringDecoder.textPlainOnly(Arrays.asList("\r\n", "\n"), false);
|
||||
|
||||
MimeType mimeType = new MimeType("text", "plain", CHARSET);
|
||||
|
||||
|
||||
@Setup(Level.Trial)
|
||||
public void setup() {
|
||||
String eventTemplate = "id:$1\n" +
|
||||
"event:some-event\n" +
|
||||
":some-comment-$1-aa\n" +
|
||||
":some-comment-$1-bb\n" +
|
||||
"data:abcdefg-$1-hijklmnop-$1-qrstuvw-$1-xyz-$1\n\n";
|
||||
|
||||
int eventLength = String.format(eventTemplate, String.format("%05d", 1)).length();
|
||||
int eventCount = this.totalSize / eventLength;
|
||||
DataBufferFactory bufferFactory = new DefaultDataBufferFactory();
|
||||
|
||||
this.chunks = Flux.range(1, eventCount)
|
||||
.map(index -> String.format(eventTemplate, String.format("%05d", index)))
|
||||
.buffer(this.chunkSize > eventLength ? this.chunkSize / eventLength : 1)
|
||||
.map(strings -> String.join("", strings))
|
||||
.map(chunk -> {
|
||||
byte[] bytes = chunk.getBytes(CHARSET);
|
||||
DataBuffer buffer = bufferFactory.allocateBuffer(bytes.length);
|
||||
buffer.write(bytes);
|
||||
return buffer;
|
||||
})
|
||||
.collectList()
|
||||
.block();
|
||||
}
|
||||
|
||||
public Flux<String> parseLines() {
|
||||
Flux<DataBuffer> input = Flux.fromIterable(this.chunks).doOnNext(DataBufferUtils::retain);
|
||||
return this.decoder.decode(input, ELEMENT_TYPE, this.mimeType, Collections.emptyMap());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,7 +53,7 @@ public interface Opcodes {
|
||||
/**
|
||||
* <i>Experimental, use at your own risk. This field will be renamed when it becomes stable, this
|
||||
* will break existing code using it. Only code compiled with --enable-preview can use this.</i>
|
||||
* <p>SPRING PATCH: no preview mode check for ASM 9 experimental, enabling it by default.
|
||||
* <p>SPRING PATCH: no preview mode check for ASM 10 experimental, enabling it by default.
|
||||
*/
|
||||
int ASM10_EXPERIMENTAL = 1 << 24 | 10 << 16 | 0 << 8;
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@ import org.springframework.asm.Opcodes;
|
||||
final class AsmApi {
|
||||
|
||||
/**
|
||||
* SPRING PATCH: always returns ASM7.
|
||||
* SPRING PATCH: always returns ASM9.
|
||||
*/
|
||||
static int value() {
|
||||
return Opcodes.ASM7;
|
||||
return Opcodes.ASM9;
|
||||
}
|
||||
|
||||
private AsmApi() {
|
||||
|
||||
@@ -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.
|
||||
@@ -225,11 +225,11 @@ public final class Conventions {
|
||||
if (!attributeName.contains("-")) {
|
||||
return attributeName;
|
||||
}
|
||||
char[] chars = attributeName.toCharArray();
|
||||
char[] result = new char[chars.length -1]; // not completely accurate but good guess
|
||||
char[] result = new char[attributeName.length() -1]; // not completely accurate but good guess
|
||||
int currPos = 0;
|
||||
boolean upperCaseNext = false;
|
||||
for (char c : chars) {
|
||||
for (int i = 0; i < attributeName.length(); i++ ) {
|
||||
char c = attributeName.charAt(i);
|
||||
if (c == '-') {
|
||||
upperCaseNext = true;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,6 @@ public abstract class KotlinDetector {
|
||||
|
||||
/**
|
||||
* Return {@code true} if the method is a suspending function.
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.3
|
||||
*/
|
||||
public static boolean isSuspendingFunction(Method method) {
|
||||
|
||||
@@ -264,14 +264,12 @@ public class ReactiveAdapterRegistry {
|
||||
registry.registerReactiveType(
|
||||
ReactiveTypeDescriptor.multiValue(io.reactivex.Observable.class, io.reactivex.Observable::empty),
|
||||
source -> ((io.reactivex.Observable<?>) source).toFlowable(io.reactivex.BackpressureStrategy.BUFFER),
|
||||
source -> io.reactivex.Flowable.fromPublisher(source)
|
||||
.toObservable()
|
||||
io.reactivex.Observable::fromPublisher
|
||||
);
|
||||
registry.registerReactiveType(
|
||||
ReactiveTypeDescriptor.singleRequiredValue(io.reactivex.Single.class),
|
||||
source -> ((io.reactivex.Single<?>) source).toFlowable(),
|
||||
source -> io.reactivex.Flowable.fromPublisher(source)
|
||||
.toObservable().singleElement().toSingle()
|
||||
io.reactivex.Single::fromPublisher
|
||||
);
|
||||
registry.registerReactiveType(
|
||||
ReactiveTypeDescriptor.singleOptionalValue(io.reactivex.Maybe.class, io.reactivex.Maybe::empty),
|
||||
@@ -282,8 +280,7 @@ public class ReactiveAdapterRegistry {
|
||||
registry.registerReactiveType(
|
||||
ReactiveTypeDescriptor.noValue(io.reactivex.Completable.class, io.reactivex.Completable::complete),
|
||||
source -> ((io.reactivex.Completable) source).toFlowable(),
|
||||
source -> io.reactivex.Flowable.fromPublisher(source)
|
||||
.toObservable().ignoreElements()
|
||||
io.reactivex.Completable::fromPublisher
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -304,30 +301,26 @@ public class ReactiveAdapterRegistry {
|
||||
io.reactivex.rxjava3.core.Observable::empty),
|
||||
source -> ((io.reactivex.rxjava3.core.Observable<?>) source).toFlowable(
|
||||
io.reactivex.rxjava3.core.BackpressureStrategy.BUFFER),
|
||||
source -> io.reactivex.rxjava3.core.Flowable.fromPublisher(source)
|
||||
.toObservable()
|
||||
io.reactivex.rxjava3.core.Observable::fromPublisher
|
||||
);
|
||||
registry.registerReactiveType(
|
||||
ReactiveTypeDescriptor.singleRequiredValue(io.reactivex.rxjava3.core.Single.class),
|
||||
source -> ((io.reactivex.rxjava3.core.Single<?>) source).toFlowable(),
|
||||
source -> io.reactivex.rxjava3.core.Flowable.fromPublisher(source)
|
||||
.toObservable().singleElement().toSingle()
|
||||
io.reactivex.rxjava3.core.Single::fromPublisher
|
||||
);
|
||||
registry.registerReactiveType(
|
||||
ReactiveTypeDescriptor.singleOptionalValue(
|
||||
io.reactivex.rxjava3.core.Maybe.class,
|
||||
io.reactivex.rxjava3.core.Maybe::empty),
|
||||
source -> ((io.reactivex.rxjava3.core.Maybe<?>) source).toFlowable(),
|
||||
source -> io.reactivex.rxjava3.core.Flowable.fromPublisher(source)
|
||||
.toObservable().singleElement()
|
||||
io.reactivex.rxjava3.core.Maybe::fromPublisher
|
||||
);
|
||||
registry.registerReactiveType(
|
||||
ReactiveTypeDescriptor.noValue(
|
||||
io.reactivex.rxjava3.core.Completable.class,
|
||||
io.reactivex.rxjava3.core.Completable::complete),
|
||||
source -> ((io.reactivex.rxjava3.core.Completable) source).toFlowable(),
|
||||
source -> io.reactivex.rxjava3.core.Flowable.fromPublisher(source)
|
||||
.toObservable().ignoreElements()
|
||||
io.reactivex.rxjava3.core.Completable::fromPublisher
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,6 @@ import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -50,8 +47,6 @@ public final class SpringProperties {
|
||||
|
||||
private static final String PROPERTIES_RESOURCE_LOCATION = "spring.properties";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SpringProperties.class);
|
||||
|
||||
private static final Properties localProperties = new Properties();
|
||||
|
||||
|
||||
@@ -61,16 +56,13 @@ public final class SpringProperties {
|
||||
URL url = (cl != null ? cl.getResource(PROPERTIES_RESOURCE_LOCATION) :
|
||||
ClassLoader.getSystemResource(PROPERTIES_RESOURCE_LOCATION));
|
||||
if (url != null) {
|
||||
logger.debug("Found 'spring.properties' file in local classpath");
|
||||
try (InputStream is = url.openStream()) {
|
||||
localProperties.load(is);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Could not load 'spring.properties' file from local classpath: " + ex);
|
||||
}
|
||||
System.err.println("Could not load 'spring.properties' file from local classpath: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,9 +100,7 @@ public final class SpringProperties {
|
||||
value = System.getProperty(key);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not retrieve system property '" + key + "': " + ex);
|
||||
}
|
||||
System.err.println("Could not retrieve system property '" + key + "': " + ex);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
|
||||
+30
-17
@@ -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.
|
||||
@@ -18,8 +18,8 @@ package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
@@ -31,10 +31,11 @@ import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Collector implementations that provide various reduction operations for
|
||||
* {@link Collector} implementations that provide various reduction operations for
|
||||
* {@link MergedAnnotation} instances.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 5.2
|
||||
*/
|
||||
public abstract class MergedAnnotationCollectors {
|
||||
@@ -52,13 +53,16 @@ public abstract class MergedAnnotationCollectors {
|
||||
* Create a new {@link Collector} that accumulates merged annotations to a
|
||||
* {@link LinkedHashSet} containing {@linkplain MergedAnnotation#synthesize()
|
||||
* synthesized} versions.
|
||||
* <p>The collector returned by this method is effectively equivalent to
|
||||
* {@code Collectors.mapping(MergedAnnotation::synthesize, Collectors.toCollection(LinkedHashSet::new))}
|
||||
* but avoids the creation of a composite collector.
|
||||
* @param <A> the annotation type
|
||||
* @return a {@link Collector} which collects and synthesizes the
|
||||
* annotations into a {@link Set}
|
||||
*/
|
||||
public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, Set<A>> toAnnotationSet() {
|
||||
return Collector.of(ArrayList<A>::new, (list, annotation) -> list.add(annotation.synthesize()),
|
||||
MergedAnnotationCollectors::addAll, LinkedHashSet::new);
|
||||
return Collector.of(LinkedHashSet::new, (set, annotation) -> set.add(annotation.synthesize()),
|
||||
MergedAnnotationCollectors::combiner);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,14 +94,14 @@ public abstract class MergedAnnotationCollectors {
|
||||
IntFunction<R[]> generator) {
|
||||
|
||||
return Collector.of(ArrayList::new, (list, annotation) -> list.add(annotation.synthesize()),
|
||||
MergedAnnotationCollectors::addAll, list -> list.toArray(generator.apply(list.size())));
|
||||
MergedAnnotationCollectors::combiner, list -> list.toArray(generator.apply(list.size())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Collector} that accumulates merged annotations to an
|
||||
* Create a new {@link Collector} that accumulates merged annotations to a
|
||||
* {@link MultiValueMap} with items {@linkplain MultiValueMap#add(Object, Object)
|
||||
* added} from each merged annotation
|
||||
* {@link MergedAnnotation#asMap(Adapt...) as a map}.
|
||||
* {@linkplain MergedAnnotation#asMap(Adapt...) as a map}.
|
||||
* @param <A> the annotation type
|
||||
* @param adaptations the adaptations that should be applied to the annotation values
|
||||
* @return a {@link Collector} which collects and synthesizes the
|
||||
@@ -111,13 +115,13 @@ public abstract class MergedAnnotationCollectors {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Collector} that accumulates merged annotations to an
|
||||
* Create a new {@link Collector} that accumulates merged annotations to a
|
||||
* {@link MultiValueMap} with items {@linkplain MultiValueMap#add(Object, Object)
|
||||
* added} from each merged annotation
|
||||
* {@link MergedAnnotation#asMap(Adapt...) as a map}.
|
||||
* {@linkplain MergedAnnotation#asMap(Adapt...) as a map}.
|
||||
* @param <A> the annotation type
|
||||
* @param adaptations the adaptations that should be applied to the annotation values
|
||||
* @param finisher the finisher function for the new {@link MultiValueMap}
|
||||
* @param adaptations the adaptations that should be applied to the annotation values
|
||||
* @return a {@link Collector} which collects and synthesizes the
|
||||
* annotations into a {@link LinkedMultiValueMap}
|
||||
* @see #toMultiValueMap(MergedAnnotation.Adapt...)
|
||||
@@ -130,7 +134,7 @@ public abstract class MergedAnnotationCollectors {
|
||||
IDENTITY_FINISH_CHARACTERISTICS : NO_CHARACTERISTICS);
|
||||
return Collector.of(LinkedMultiValueMap::new,
|
||||
(map, annotation) -> annotation.asMap(adaptations).forEach(map::add),
|
||||
MergedAnnotationCollectors::merge, finisher, characteristics);
|
||||
MergedAnnotationCollectors::combiner, finisher, characteristics);
|
||||
}
|
||||
|
||||
|
||||
@@ -138,13 +142,22 @@ public abstract class MergedAnnotationCollectors {
|
||||
return instance == candidate;
|
||||
}
|
||||
|
||||
private static <E, L extends List<E>> L addAll(L list, L additions) {
|
||||
list.addAll(additions);
|
||||
return list;
|
||||
/**
|
||||
* {@link Collector#combiner() Combiner} for collections.
|
||||
* <p>This method is only invoked if the {@link java.util.stream.Stream} is
|
||||
* processed in {@linkplain java.util.stream.Stream#parallel() parallel}.
|
||||
*/
|
||||
private static <E, C extends Collection<E>> C combiner(C collection, C additions) {
|
||||
collection.addAll(additions);
|
||||
return collection;
|
||||
}
|
||||
|
||||
private static <K, V> MultiValueMap<K, V> merge(MultiValueMap<K, V> map,
|
||||
MultiValueMap<K, V> additions) {
|
||||
/**
|
||||
* {@link Collector#combiner() Combiner} for multi-value maps.
|
||||
* <p>This method is only invoked if the {@link java.util.stream.Stream} is
|
||||
* processed in {@linkplain java.util.stream.Stream#parallel() parallel}.
|
||||
*/
|
||||
private static <K, V> MultiValueMap<K, V> combiner(MultiValueMap<K, V> map, MultiValueMap<K, V> additions) {
|
||||
map.addAll(additions);
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@@ -148,4 +150,22 @@ public abstract class Hints {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the hints contain a {@link #LOG_PREFIX_HINT} and the given logger has
|
||||
* DEBUG level enabled, apply the log prefix as a hint to the given buffer
|
||||
* via {@link DataBufferUtils#touch(DataBuffer, Object)}.
|
||||
* @param buffer the buffer to touch
|
||||
* @param hints the hints map to check for a log prefix
|
||||
* @param logger the logger whose level to check
|
||||
* @since 5.3.2
|
||||
*/
|
||||
public static void touchDataBuffer(DataBuffer buffer, @Nullable Map<String, Object> hints, Log logger) {
|
||||
if (logger.isDebugEnabled() && hints != null) {
|
||||
Object logPrefix = hints.get(LOG_PREFIX_HINT);
|
||||
if (logPrefix != null) {
|
||||
DataBufferUtils.touch(buffer, logPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -130,6 +130,9 @@ public class ResourceRegionEncoder extends AbstractEncoder<ResourceRegion> {
|
||||
}
|
||||
|
||||
Flux<DataBuffer> in = DataBufferUtils.read(resource, position, bufferFactory, this.bufferSize);
|
||||
if (logger.isDebugEnabled()) {
|
||||
in = in.doOnNext(buffer -> Hints.touchDataBuffer(buffer, hints, logger));
|
||||
}
|
||||
return DataBufferUtils.takeUntilByteCount(in, count);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,21 +21,20 @@ import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferLimitException;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.DataBufferWrapper;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.core.io.buffer.LimitedDataBufferList;
|
||||
import org.springframework.core.io.buffer.PooledDataBuffer;
|
||||
import org.springframework.core.log.LogFormatUtils;
|
||||
@@ -45,12 +44,12 @@ import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
/**
|
||||
* Decode from a data buffer stream to a {@code String} stream. Before decoding, this decoder
|
||||
* realigns the incoming data buffers so that each buffer ends with a newline.
|
||||
* This is to make sure that multibyte characters are decoded properly, and do not cross buffer
|
||||
* boundaries. The default delimiters ({@code \n}, {@code \r\n})can be customized.
|
||||
*
|
||||
* <p>Partially inspired by Netty's {@code DelimiterBasedFrameDecoder}.
|
||||
* Decode from a data buffer stream to a {@code String} stream, either splitting
|
||||
* or aggregating incoming data chunks to realign along newlines delimiters
|
||||
* and produce a stream of strings. This is useful for streaming but is also
|
||||
* necessary to ensure that that multibyte characters can be decoded correctly,
|
||||
* avoiding split-character issues. The default delimiters used by default are
|
||||
* {@code \n} and {@code \r\n} but that can be customized.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Brian Clozel
|
||||
@@ -115,21 +114,22 @@ public final class StringDecoder extends AbstractDataBufferDecoder<String> {
|
||||
|
||||
byte[][] delimiterBytes = getDelimiterBytes(mimeType);
|
||||
|
||||
Flux<DataBuffer> inputFlux = Flux.defer(() -> {
|
||||
DataBufferUtils.Matcher matcher = DataBufferUtils.matcher(delimiterBytes);
|
||||
LimitedDataBufferList chunks = new LimitedDataBufferList(getMaxInMemorySize());
|
||||
DataBufferUtils.Matcher matcher = DataBufferUtils.matcher(delimiterBytes);
|
||||
|
||||
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
|
||||
LimitChecker limiter = new LimitChecker(getMaxInMemorySize());
|
||||
|
||||
return Flux.from(input)
|
||||
.concatMapIterable(buffer -> endFrameAfterDelimiter(buffer, matcher))
|
||||
.doOnNext(limiter)
|
||||
.bufferUntil(buffer -> buffer instanceof EndFrameBuffer)
|
||||
.map(list -> joinAndStrip(list, this.stripDelimiter))
|
||||
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
|
||||
});
|
||||
|
||||
return super.decode(inputFlux, elementType, mimeType, hints);
|
||||
return Flux.from(input)
|
||||
.concatMapIterable(buffer -> processDataBuffer(buffer, matcher, chunks))
|
||||
.concatWith(Mono.defer(() -> {
|
||||
if (chunks.isEmpty()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
DataBuffer lastBuffer = chunks.get(0).factory().join(chunks);
|
||||
chunks.clear();
|
||||
return Mono.just(lastBuffer);
|
||||
}))
|
||||
.doOnTerminate(chunks::releaseAndClear)
|
||||
.doOnDiscard(PooledDataBuffer.class, PooledDataBuffer::release)
|
||||
.map(buffer -> decode(buffer, elementType, mimeType, hints));
|
||||
}
|
||||
|
||||
private byte[][] getDelimiterBytes(@Nullable MimeType mimeType) {
|
||||
@@ -142,6 +142,43 @@ public final class StringDecoder extends AbstractDataBufferDecoder<String> {
|
||||
});
|
||||
}
|
||||
|
||||
private Collection<DataBuffer> processDataBuffer(
|
||||
DataBuffer buffer, DataBufferUtils.Matcher matcher, LimitedDataBufferList chunks) {
|
||||
|
||||
try {
|
||||
List<DataBuffer> result = null;
|
||||
do {
|
||||
int endIndex = matcher.match(buffer);
|
||||
if (endIndex == -1) {
|
||||
chunks.add(buffer);
|
||||
DataBufferUtils.retain(buffer); // retain after add (may raise DataBufferLimitException)
|
||||
break;
|
||||
}
|
||||
int startIndex = buffer.readPosition();
|
||||
int length = (endIndex - startIndex + 1);
|
||||
DataBuffer slice = buffer.retainedSlice(startIndex, length);
|
||||
if (this.stripDelimiter) {
|
||||
slice.writePosition(slice.writePosition() - matcher.delimiter().length);
|
||||
}
|
||||
result = (result != null ? result : new ArrayList<>());
|
||||
if (chunks.isEmpty()) {
|
||||
result.add(slice);
|
||||
}
|
||||
else {
|
||||
chunks.add(slice);
|
||||
result.add(buffer.factory().join(chunks));
|
||||
chunks.clear();
|
||||
}
|
||||
buffer.readPosition(endIndex + 1);
|
||||
}
|
||||
while (buffer.readableByteCount() > 0);
|
||||
return (result != null ? result : Collections.emptyList());
|
||||
}
|
||||
finally {
|
||||
DataBufferUtils.release(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decode(DataBuffer dataBuffer, ResolvableType elementType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
@@ -166,68 +203,6 @@ public final class StringDecoder extends AbstractDataBufferDecoder<String> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first match and longest delimiter, {@link EndFrameBuffer} just after it.
|
||||
* @param dataBuffer the buffer to find delimiters in
|
||||
* @param matcher used to find the first delimiters
|
||||
* @return a flux of buffers, containing {@link EndFrameBuffer} after each delimiter that was
|
||||
* found in {@code dataBuffer}. Returns Flux, because returning List (w/ flatMapIterable)
|
||||
* results in memory leaks due to pre-fetching.
|
||||
*/
|
||||
private static List<DataBuffer> endFrameAfterDelimiter(DataBuffer dataBuffer, DataBufferUtils.Matcher matcher) {
|
||||
List<DataBuffer> result = new ArrayList<>();
|
||||
try {
|
||||
do {
|
||||
int endIdx = matcher.match(dataBuffer);
|
||||
if (endIdx != -1) {
|
||||
int readPosition = dataBuffer.readPosition();
|
||||
int length = (endIdx - readPosition + 1);
|
||||
DataBuffer slice = dataBuffer.retainedSlice(readPosition, length);
|
||||
result.add(slice);
|
||||
result.add(new EndFrameBuffer(matcher.delimiter()));
|
||||
dataBuffer.readPosition(endIdx + 1);
|
||||
}
|
||||
else {
|
||||
result.add(DataBufferUtils.retain(dataBuffer));
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (dataBuffer.readableByteCount() > 0);
|
||||
}
|
||||
finally {
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins the given list of buffers. If the list ends with a {@link EndFrameBuffer}, it is
|
||||
* removed. If {@code stripDelimiter} is {@code true} and the resulting buffer ends with
|
||||
* a delimiter, it is removed.
|
||||
* @param dataBuffers the data buffers to join
|
||||
* @param stripDelimiter whether to strip the delimiter
|
||||
* @return the joined buffer
|
||||
*/
|
||||
private static DataBuffer joinAndStrip(List<DataBuffer> dataBuffers, boolean stripDelimiter) {
|
||||
Assert.state(!dataBuffers.isEmpty(), "DataBuffers should not be empty");
|
||||
|
||||
byte[] matchingDelimiter = null;
|
||||
|
||||
int lastIdx = dataBuffers.size() - 1;
|
||||
DataBuffer lastBuffer = dataBuffers.get(lastIdx);
|
||||
if (lastBuffer instanceof EndFrameBuffer) {
|
||||
matchingDelimiter = ((EndFrameBuffer) lastBuffer).delimiter();
|
||||
dataBuffers.remove(lastIdx);
|
||||
}
|
||||
|
||||
DataBuffer result = dataBuffers.get(0).factory().join(dataBuffers);
|
||||
if (stripDelimiter && matchingDelimiter != null) {
|
||||
result.writePosition(result.writePosition() - matchingDelimiter.length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a {@code StringDecoder} for {@code "text/plain"}.
|
||||
* @param stripDelimiter this flag is ignored
|
||||
@@ -285,46 +260,4 @@ public final class StringDecoder extends AbstractDataBufferDecoder<String> {
|
||||
new MimeType("text", "plain", DEFAULT_CHARSET), MimeTypeUtils.ALL);
|
||||
}
|
||||
|
||||
|
||||
private static class EndFrameBuffer extends DataBufferWrapper {
|
||||
|
||||
private static final DataBuffer BUFFER = DefaultDataBufferFactory.sharedInstance.wrap(new byte[0]);
|
||||
|
||||
private final byte[] delimiter;
|
||||
|
||||
public EndFrameBuffer(byte[] delimiter) {
|
||||
super(BUFFER);
|
||||
this.delimiter = delimiter;
|
||||
}
|
||||
|
||||
public byte[] delimiter() {
|
||||
return this.delimiter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class LimitChecker implements Consumer<DataBuffer> {
|
||||
|
||||
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
|
||||
private final LimitedDataBufferList list;
|
||||
|
||||
LimitChecker(int maxInMemorySize) {
|
||||
this.list = new LimitedDataBufferList(maxInMemorySize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(DataBuffer buffer) {
|
||||
if (buffer instanceof EndFrameBuffer) {
|
||||
this.list.clear();
|
||||
}
|
||||
try {
|
||||
this.list.add(buffer);
|
||||
}
|
||||
catch (DataBufferLimitException ex) {
|
||||
DataBufferUtils.release(buffer);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -118,7 +118,7 @@ public final class Property {
|
||||
}
|
||||
|
||||
|
||||
// package private
|
||||
// Package private
|
||||
|
||||
MethodParameter getMethodParameter() {
|
||||
return this.methodParameter;
|
||||
@@ -132,7 +132,7 @@ public final class Property {
|
||||
}
|
||||
|
||||
|
||||
// internal helpers
|
||||
// Internal helpers
|
||||
|
||||
private String resolveName() {
|
||||
if (this.readMethod != null) {
|
||||
@@ -142,10 +142,13 @@ public final class Property {
|
||||
}
|
||||
else {
|
||||
index = this.readMethod.getName().indexOf("is");
|
||||
if (index == -1) {
|
||||
throw new IllegalArgumentException("Not a getter method");
|
||||
if (index != -1) {
|
||||
index += 2;
|
||||
}
|
||||
else {
|
||||
// Record-style plain accessor method, e.g. name()
|
||||
index = 0;
|
||||
}
|
||||
index += 2;
|
||||
}
|
||||
return StringUtils.uncapitalize(this.readMethod.getName().substring(index));
|
||||
}
|
||||
|
||||
+7
-8
@@ -17,17 +17,17 @@
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import org.springframework.core.DecoratingProxy;
|
||||
import org.springframework.core.ResolvableType;
|
||||
@@ -500,9 +500,9 @@ public class GenericConversionService implements ConfigurableConversionService {
|
||||
*/
|
||||
private static class Converters {
|
||||
|
||||
private final Set<GenericConverter> globalConverters = new LinkedHashSet<>();
|
||||
private final Set<GenericConverter> globalConverters = new CopyOnWriteArraySet<>();
|
||||
|
||||
private final Map<ConvertiblePair, ConvertersForPair> converters = new LinkedHashMap<>(36);
|
||||
private final Map<ConvertiblePair, ConvertersForPair> converters = new ConcurrentHashMap<>(256);
|
||||
|
||||
public void add(GenericConverter converter) {
|
||||
Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
|
||||
@@ -513,8 +513,7 @@ public class GenericConversionService implements ConfigurableConversionService {
|
||||
}
|
||||
else {
|
||||
for (ConvertiblePair convertiblePair : convertibleTypes) {
|
||||
ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair);
|
||||
convertersForPair.add(converter);
|
||||
getMatchableConverters(convertiblePair).add(converter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -652,7 +651,7 @@ public class GenericConversionService implements ConfigurableConversionService {
|
||||
*/
|
||||
private static class ConvertersForPair {
|
||||
|
||||
private final Deque<GenericConverter> converters = new ArrayDeque<>(1);
|
||||
private final Deque<GenericConverter> converters = new ConcurrentLinkedDeque<>();
|
||||
|
||||
public void add(GenericConverter converter) {
|
||||
this.converters.addFirst(converter);
|
||||
|
||||
+325
-190
@@ -30,7 +30,6 @@ import java.nio.channels.WritableByteChannel;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
@@ -488,6 +487,24 @@ public abstract class DataBufferUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate the given hint with the data buffer if it is a pooled buffer
|
||||
* and supports leak tracking.
|
||||
* @param dataBuffer the data buffer to attach the hint to
|
||||
* @param hint the hint to attach
|
||||
* @return the input buffer
|
||||
* @since 5.3.2
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends DataBuffer> T touch(T dataBuffer, Object hint) {
|
||||
if (dataBuffer instanceof PooledDataBuffer) {
|
||||
return (T) ((PooledDataBuffer) dataBuffer).touch(hint);
|
||||
}
|
||||
else {
|
||||
return dataBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the given data buffer, if it is a {@link PooledDataBuffer} and
|
||||
* has been {@linkplain PooledDataBuffer#isAllocated() allocated}.
|
||||
@@ -568,65 +585,290 @@ public abstract class DataBufferUtils {
|
||||
|
||||
/**
|
||||
* Return a {@link Matcher} for the given delimiter.
|
||||
* The matcher can be used to find the delimiters in data buffers.
|
||||
* The matcher can be used to find the delimiters in a stream of data buffers.
|
||||
* @param delimiter the delimiter bytes to find
|
||||
* @return the matcher
|
||||
* @since 5.2
|
||||
*/
|
||||
public static Matcher matcher(byte[] delimiter) {
|
||||
Assert.isTrue(delimiter.length > 0, "Delimiter must not be empty");
|
||||
return new KnuthMorrisPrattMatcher(delimiter);
|
||||
return createMatcher(delimiter);
|
||||
}
|
||||
|
||||
/** Return a {@link Matcher} for the given delimiters.
|
||||
* The matcher can be used to find the delimiters in data buffers.
|
||||
/**
|
||||
* Return a {@link Matcher} for the given delimiters.
|
||||
* The matcher can be used to find the delimiters in a stream of data buffers.
|
||||
* @param delimiters the delimiters bytes to find
|
||||
* @return the matcher
|
||||
* @since 5.2
|
||||
*/
|
||||
public static Matcher matcher(byte[]... delimiters) {
|
||||
Assert.isTrue(delimiters.length > 0, "Delimiters must not be empty");
|
||||
if (delimiters.length == 1) {
|
||||
return matcher(delimiters[0]);
|
||||
}
|
||||
else {
|
||||
Matcher[] matchers = new Matcher[delimiters.length];
|
||||
for (int i = 0; i < delimiters.length; i++) {
|
||||
matchers[i] = matcher(delimiters[i]);
|
||||
}
|
||||
return new CompositeMatcher(matchers);
|
||||
return (delimiters.length == 1 ? createMatcher(delimiters[0]) : new CompositeMatcher(delimiters));
|
||||
}
|
||||
|
||||
private static NestedMatcher createMatcher(byte[] delimiter) {
|
||||
Assert.isTrue(delimiter.length > 0, "Delimiter must not be empty");
|
||||
switch (delimiter.length) {
|
||||
case 1:
|
||||
return (delimiter[0] == 10 ? SingleByteMatcher.NEWLINE_MATCHER : new SingleByteMatcher(delimiter));
|
||||
case 2:
|
||||
return new TwoByteMatcher(delimiter);
|
||||
default:
|
||||
return new KnuthMorrisPrattMatcher(delimiter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines an object that matches a data buffer against a delimiter.
|
||||
* Contract to find delimiter(s) against one or more data buffers that can
|
||||
* be passed one at a time to the {@link #match(DataBuffer)} method.
|
||||
*
|
||||
* @since 5.2
|
||||
* @see #match(DataBuffer)
|
||||
*/
|
||||
public interface Matcher {
|
||||
|
||||
/**
|
||||
* Returns the position of the final matching delimiter byte that matches the given buffer,
|
||||
* or {@code -1} if not found.
|
||||
* @param dataBuffer the buffer in which to search for the delimiter
|
||||
* @return the position of the final matching delimiter, or {@code -1} if not found.
|
||||
* Find the first matching delimiter and return the index of the last
|
||||
* byte of the delimiter, or {@code -1} if not found.
|
||||
*/
|
||||
int match(DataBuffer dataBuffer);
|
||||
|
||||
/**
|
||||
* Return the delimiter used for this matcher.
|
||||
* @return the delimiter
|
||||
* Return the delimiter from the last invocation of {@link #match(DataBuffer)}.
|
||||
*/
|
||||
byte[] delimiter();
|
||||
|
||||
/**
|
||||
* Resets the state of this matcher.
|
||||
* Reset the state of this matcher.
|
||||
*/
|
||||
void reset();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Matcher that supports searching for multiple delimiters.
|
||||
*/
|
||||
private static class CompositeMatcher implements Matcher {
|
||||
|
||||
private static final byte[] NO_DELIMITER = new byte[0];
|
||||
|
||||
|
||||
private final NestedMatcher[] matchers;
|
||||
|
||||
byte[] longestDelimiter = NO_DELIMITER;
|
||||
|
||||
CompositeMatcher(byte[][] delimiters) {
|
||||
this.matchers = initMatchers(delimiters);
|
||||
}
|
||||
|
||||
private static NestedMatcher[] initMatchers(byte[][] delimiters) {
|
||||
NestedMatcher[] matchers = new NestedMatcher[delimiters.length];
|
||||
for (int i = 0; i < delimiters.length; i++) {
|
||||
matchers[i] = createMatcher(delimiters[i]);
|
||||
}
|
||||
return matchers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int match(DataBuffer dataBuffer) {
|
||||
this.longestDelimiter = NO_DELIMITER;
|
||||
|
||||
for (int pos = dataBuffer.readPosition(); pos < dataBuffer.writePosition(); pos++) {
|
||||
byte b = dataBuffer.getByte(pos);
|
||||
|
||||
for (NestedMatcher matcher : this.matchers) {
|
||||
if (matcher.match(b) && matcher.delimiter().length > this.longestDelimiter.length) {
|
||||
this.longestDelimiter = matcher.delimiter();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.longestDelimiter != NO_DELIMITER) {
|
||||
reset();
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] delimiter() {
|
||||
Assert.state(this.longestDelimiter != NO_DELIMITER, "Illegal state!");
|
||||
return this.longestDelimiter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
for (NestedMatcher matcher : this.matchers) {
|
||||
matcher.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Matcher that can be nested within {@link CompositeMatcher} where multiple
|
||||
* matchers advance together using the same index, one byte at a time.
|
||||
*/
|
||||
private interface NestedMatcher extends Matcher {
|
||||
|
||||
/**
|
||||
* Perform a match against the next byte of the stream and return true
|
||||
* if the delimiter is fully matched.
|
||||
*/
|
||||
boolean match(byte b);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Matcher for a single byte delimiter.
|
||||
*/
|
||||
private static class SingleByteMatcher implements NestedMatcher {
|
||||
|
||||
static SingleByteMatcher NEWLINE_MATCHER = new SingleByteMatcher(new byte[] {10});
|
||||
|
||||
private final byte[] delimiter;
|
||||
|
||||
SingleByteMatcher(byte[] delimiter) {
|
||||
Assert.isTrue(delimiter.length == 1, "Expected a 1 byte delimiter");
|
||||
this.delimiter = delimiter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int match(DataBuffer dataBuffer) {
|
||||
for (int pos = dataBuffer.readPosition(); pos < dataBuffer.writePosition(); pos++) {
|
||||
byte b = dataBuffer.getByte(pos);
|
||||
if (match(b)) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(byte b) {
|
||||
return this.delimiter[0] == b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] delimiter() {
|
||||
return this.delimiter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Base class for a {@link NestedMatcher}.
|
||||
*/
|
||||
private static abstract class AbstractNestedMatcher implements NestedMatcher {
|
||||
|
||||
private final byte[] delimiter;
|
||||
|
||||
private int matches = 0;
|
||||
|
||||
|
||||
protected AbstractNestedMatcher(byte[] delimiter) {
|
||||
this.delimiter = delimiter;
|
||||
}
|
||||
|
||||
protected void setMatches(int index) {
|
||||
this.matches = index;
|
||||
}
|
||||
|
||||
protected int getMatches() {
|
||||
return this.matches;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int match(DataBuffer dataBuffer) {
|
||||
for (int pos = dataBuffer.readPosition(); pos < dataBuffer.writePosition(); pos++) {
|
||||
byte b = dataBuffer.getByte(pos);
|
||||
if (match(b)) {
|
||||
reset();
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(byte b) {
|
||||
if (b == this.delimiter[this.matches]) {
|
||||
this.matches++;
|
||||
return (this.matches == delimiter().length);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] delimiter() {
|
||||
return this.delimiter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.matches = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Matcher with a 2 byte delimiter that does not benefit from a
|
||||
* Knuth-Morris-Pratt suffix-prefix table.
|
||||
*/
|
||||
private static class TwoByteMatcher extends AbstractNestedMatcher {
|
||||
|
||||
protected TwoByteMatcher(byte[] delimiter) {
|
||||
super(delimiter);
|
||||
Assert.isTrue(delimiter.length == 2, "Expected a 2-byte delimiter");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of {@link Matcher} that uses the Knuth-Morris-Pratt algorithm.
|
||||
* @see <a href="https://www.nayuki.io/page/knuth-morris-pratt-string-matching">Knuth-Morris-Pratt string matching</a>
|
||||
*/
|
||||
private static class KnuthMorrisPrattMatcher extends AbstractNestedMatcher {
|
||||
|
||||
private final int[] table;
|
||||
|
||||
public KnuthMorrisPrattMatcher(byte[] delimiter) {
|
||||
super(delimiter);
|
||||
this.table = longestSuffixPrefixTable(delimiter);
|
||||
}
|
||||
|
||||
private static int[] longestSuffixPrefixTable(byte[] delimiter) {
|
||||
int[] result = new int[delimiter.length];
|
||||
result[0] = 0;
|
||||
for (int i = 1; i < delimiter.length; i++) {
|
||||
int j = result[i - 1];
|
||||
while (j > 0 && delimiter[i] != delimiter[j]) {
|
||||
j = result[j - 1];
|
||||
}
|
||||
if (delimiter[i] == delimiter[j]) {
|
||||
j++;
|
||||
}
|
||||
result[i] = j;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(byte b) {
|
||||
while (getMatches() > 0 && b != delimiter()[getMatches()]) {
|
||||
setMatches(this.table[getMatches() - 1]);
|
||||
}
|
||||
return super.match(b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ReadableByteChannelGenerator implements Consumer<SynchronousSink<DataBuffer>> {
|
||||
|
||||
private final ReadableByteChannel channel;
|
||||
@@ -683,9 +925,7 @@ public abstract class DataBufferUtils {
|
||||
|
||||
private final AtomicLong position;
|
||||
|
||||
private final AtomicBoolean reading = new AtomicBoolean();
|
||||
|
||||
private final AtomicBoolean disposed = new AtomicBoolean();
|
||||
private final AtomicReference<State> state = new AtomicReference<>(State.IDLE);
|
||||
|
||||
public ReadCompletionHandler(AsynchronousFileChannel channel,
|
||||
FluxSink<DataBuffer> sink, long position, DataBufferFactory dataBufferFactory, int bufferSize) {
|
||||
@@ -697,39 +937,68 @@ public abstract class DataBufferUtils {
|
||||
this.bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
public void read() {
|
||||
if (this.sink.requestedFromDownstream() > 0 &&
|
||||
isNotDisposed() &&
|
||||
this.reading.compareAndSet(false, true)) {
|
||||
DataBuffer dataBuffer = this.dataBufferFactory.allocateBuffer(this.bufferSize);
|
||||
ByteBuffer byteBuffer = dataBuffer.asByteBuffer(0, this.bufferSize);
|
||||
this.channel.read(byteBuffer, this.position.get(), dataBuffer, this);
|
||||
/**
|
||||
* Invoked when Reactive Streams consumer signals demand.
|
||||
*/
|
||||
public void request(long n) {
|
||||
tryRead();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when Reactive Streams consumer cancels.
|
||||
*/
|
||||
public void cancel() {
|
||||
this.state.getAndSet(State.DISPOSED);
|
||||
|
||||
// According java.nio.channels.AsynchronousChannel "if an I/O operation is outstanding
|
||||
// on the channel and the channel's close method is invoked, then the I/O operation
|
||||
// fails with the exception AsynchronousCloseException". That should invoke the failed
|
||||
// callback below and the current DataBuffer should be released.
|
||||
|
||||
closeChannel(this.channel);
|
||||
}
|
||||
|
||||
private void tryRead() {
|
||||
if (this.sink.requestedFromDownstream() > 0 && this.state.compareAndSet(State.IDLE, State.READING)) {
|
||||
read();
|
||||
}
|
||||
}
|
||||
|
||||
private void read() {
|
||||
DataBuffer dataBuffer = this.dataBufferFactory.allocateBuffer(this.bufferSize);
|
||||
ByteBuffer byteBuffer = dataBuffer.asByteBuffer(0, this.bufferSize);
|
||||
this.channel.read(byteBuffer, this.position.get(), dataBuffer, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completed(Integer read, DataBuffer dataBuffer) {
|
||||
if (isNotDisposed()) {
|
||||
if (read != -1) {
|
||||
this.position.addAndGet(read);
|
||||
dataBuffer.writePosition(read);
|
||||
this.sink.next(dataBuffer);
|
||||
this.reading.set(false);
|
||||
read();
|
||||
}
|
||||
else {
|
||||
release(dataBuffer);
|
||||
closeChannel(this.channel);
|
||||
if (this.disposed.compareAndSet(false, true)) {
|
||||
this.sink.complete();
|
||||
}
|
||||
this.reading.set(false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this.state.get().equals(State.DISPOSED)) {
|
||||
release(dataBuffer);
|
||||
closeChannel(this.channel);
|
||||
this.reading.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (read == -1) {
|
||||
release(dataBuffer);
|
||||
closeChannel(this.channel);
|
||||
this.state.set(State.DISPOSED);
|
||||
this.sink.complete();
|
||||
return;
|
||||
}
|
||||
|
||||
this.position.addAndGet(read);
|
||||
dataBuffer.writePosition(read);
|
||||
this.sink.next(dataBuffer);
|
||||
|
||||
// Stay in READING mode if there is demand
|
||||
if (this.sink.requestedFromDownstream() > 0) {
|
||||
read();
|
||||
return;
|
||||
}
|
||||
|
||||
// Release READING mode and then try again in case of concurrent "request"
|
||||
if (this.state.compareAndSet(State.READING, State.IDLE)) {
|
||||
tryRead();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,26 +1006,12 @@ public abstract class DataBufferUtils {
|
||||
public void failed(Throwable exc, DataBuffer dataBuffer) {
|
||||
release(dataBuffer);
|
||||
closeChannel(this.channel);
|
||||
if (this.disposed.compareAndSet(false, true)) {
|
||||
this.sink.error(exc);
|
||||
}
|
||||
this.reading.set(false);
|
||||
this.state.set(State.DISPOSED);
|
||||
this.sink.error(exc);
|
||||
}
|
||||
|
||||
public void request(long n) {
|
||||
read();
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
if (this.disposed.compareAndSet(false, true)) {
|
||||
if (!this.reading.get()) {
|
||||
closeChannel(this.channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNotDisposed() {
|
||||
return !this.disposed.get();
|
||||
private enum State {
|
||||
IDLE, READING, DISPOSED
|
||||
}
|
||||
}
|
||||
|
||||
@@ -895,124 +1150,4 @@ public abstract class DataBufferUtils {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of {@link Matcher} that uses the Knuth-Morris-Pratt algorithm.
|
||||
* @see <a href="https://www.nayuki.io/page/knuth-morris-pratt-string-matching">Knuth-Morris-Pratt string matching</a>
|
||||
*/
|
||||
private static class KnuthMorrisPrattMatcher implements Matcher {
|
||||
|
||||
private final byte[] delimiter;
|
||||
|
||||
private final int[] table;
|
||||
|
||||
private int matches = 0;
|
||||
|
||||
public KnuthMorrisPrattMatcher(byte[] delimiter) {
|
||||
this.delimiter = Arrays.copyOf(delimiter, delimiter.length);
|
||||
this.table = longestSuffixPrefixTable(delimiter);
|
||||
}
|
||||
|
||||
private static int[] longestSuffixPrefixTable(byte[] delimiter) {
|
||||
int[] result = new int[delimiter.length];
|
||||
result[0] = 0;
|
||||
for (int i = 1; i < delimiter.length; i++) {
|
||||
int j = result[i - 1];
|
||||
while (j > 0 && delimiter[i] != delimiter[j]) {
|
||||
j = result[j - 1];
|
||||
}
|
||||
if (delimiter[i] == delimiter[j]) {
|
||||
j++;
|
||||
}
|
||||
result[i] = j;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int match(DataBuffer dataBuffer) {
|
||||
for (int i = dataBuffer.readPosition(); i < dataBuffer.writePosition(); i++) {
|
||||
byte b = dataBuffer.getByte(i);
|
||||
|
||||
while (this.matches > 0 && b != this.delimiter[this.matches]) {
|
||||
this.matches = this.table[this.matches - 1];
|
||||
}
|
||||
|
||||
if (b == this.delimiter[this.matches]) {
|
||||
this.matches++;
|
||||
if (this.matches == this.delimiter.length) {
|
||||
reset();
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] delimiter() {
|
||||
return Arrays.copyOf(this.delimiter, this.delimiter.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.matches = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of {@link Matcher} that wraps several other matchers.
|
||||
*/
|
||||
private static class CompositeMatcher implements Matcher {
|
||||
|
||||
private static final byte[] NO_DELIMITER = new byte[0];
|
||||
|
||||
private final Matcher[] matchers;
|
||||
|
||||
byte[] longestDelimiter = NO_DELIMITER;
|
||||
|
||||
public CompositeMatcher(Matcher[] matchers) {
|
||||
this.matchers = matchers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int match(DataBuffer dataBuffer) {
|
||||
this.longestDelimiter = NO_DELIMITER;
|
||||
int bestEndIdx = Integer.MAX_VALUE;
|
||||
|
||||
|
||||
for (Matcher matcher : this.matchers) {
|
||||
int endIdx = matcher.match(dataBuffer);
|
||||
if (endIdx != -1 &&
|
||||
endIdx <= bestEndIdx &&
|
||||
matcher.delimiter().length > this.longestDelimiter.length) {
|
||||
bestEndIdx = endIdx;
|
||||
this.longestDelimiter = matcher.delimiter();
|
||||
}
|
||||
}
|
||||
if (bestEndIdx == Integer.MAX_VALUE) {
|
||||
this.longestDelimiter = NO_DELIMITER;
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
reset();
|
||||
return bestEndIdx;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] delimiter() {
|
||||
Assert.state(this.longestDelimiter != NO_DELIMITER, "Illegal state!");
|
||||
return this.longestDelimiter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
for (Matcher matcher : this.matchers) {
|
||||
matcher.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-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.
|
||||
@@ -54,11 +54,8 @@ public class LimitedDataBufferList extends ArrayList<DataBuffer> {
|
||||
|
||||
@Override
|
||||
public boolean add(DataBuffer buffer) {
|
||||
boolean result = super.add(buffer);
|
||||
if (result) {
|
||||
updateCount(buffer.readableByteCount());
|
||||
}
|
||||
return result;
|
||||
updateCount(buffer.readableByteCount());
|
||||
return super.add(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -315,6 +315,12 @@ public class NettyDataBuffer implements PooledDataBuffer {
|
||||
return new NettyDataBuffer(this.byteBuf.retain(), this.dataBufferFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledDataBuffer touch(Object hint) {
|
||||
this.byteBuf.touch(hint);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean release() {
|
||||
return this.byteBuf.release();
|
||||
|
||||
@@ -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.
|
||||
@@ -38,6 +38,13 @@ public interface PooledDataBuffer extends DataBuffer {
|
||||
*/
|
||||
PooledDataBuffer retain();
|
||||
|
||||
/**
|
||||
* Associate the given hint with the data buffer for debugging purposes.
|
||||
* @return this buffer
|
||||
* @since 5.3.2
|
||||
*/
|
||||
PooledDataBuffer touch(Object hint);
|
||||
|
||||
/**
|
||||
* Decrease the reference count for this buffer by one,
|
||||
* and deallocate it once the count reaches zero.
|
||||
|
||||
+3
@@ -432,6 +432,9 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
||||
// Possibly "c:" drive prefix on Windows, to be upper-cased for proper duplicate detection
|
||||
filePath = StringUtils.capitalize(filePath);
|
||||
}
|
||||
// # can appear in directories/filenames, java.net.URL should not treat it as a fragment
|
||||
filePath = StringUtils.replace(filePath, "#", "%23");
|
||||
// Build URL that points to the root of the jar file
|
||||
UrlResource jarResource = new UrlResource(ResourceUtils.JAR_URL_PREFIX +
|
||||
ResourceUtils.FILE_URL_PREFIX + filePath + ResourceUtils.JAR_URL_SEPARATOR);
|
||||
// Potentially overlapping with URLClassLoader.getURLs() result above!
|
||||
|
||||
+7
-13
@@ -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.
|
||||
@@ -18,10 +18,11 @@ package org.springframework.core.io.support;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -129,7 +130,7 @@ public class ResourceArrayPropertyEditor extends PropertyEditorSupport {
|
||||
public void setValue(Object value) throws IllegalArgumentException {
|
||||
if (value instanceof Collection || (value instanceof Object[] && !(value instanceof Resource[]))) {
|
||||
Collection<?> input = (value instanceof Collection ? (Collection<?>) value : Arrays.asList((Object[]) value));
|
||||
List<Resource> merged = new ArrayList<>();
|
||||
Set<Resource> merged = new LinkedHashSet<>();
|
||||
for (Object element : input) {
|
||||
if (element instanceof String) {
|
||||
// A location pattern: resolve it into a Resource array.
|
||||
@@ -137,11 +138,7 @@ public class ResourceArrayPropertyEditor extends PropertyEditorSupport {
|
||||
String pattern = resolvePath((String) element).trim();
|
||||
try {
|
||||
Resource[] resources = this.resourcePatternResolver.getResources(pattern);
|
||||
for (Resource resource : resources) {
|
||||
if (!merged.contains(resource)) {
|
||||
merged.add(resource);
|
||||
}
|
||||
}
|
||||
Collections.addAll(merged, resources);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// ignore - might be an unresolved placeholder or non-existing base directory
|
||||
@@ -152,10 +149,7 @@ public class ResourceArrayPropertyEditor extends PropertyEditorSupport {
|
||||
}
|
||||
else if (element instanceof Resource) {
|
||||
// A Resource object: add it to the result.
|
||||
Resource resource = (Resource) element;
|
||||
if (!merged.contains(resource)) {
|
||||
merged.add(resource);
|
||||
}
|
||||
merged.add((Resource) element);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Cannot convert element [" + element + "] to [" +
|
||||
|
||||
+7
-4
@@ -24,10 +24,13 @@ import org.springframework.core.metrics.StartupStep;
|
||||
|
||||
/**
|
||||
* {@link ApplicationStartup} implementation for the Java Flight Recorder.
|
||||
* <p>This variant records {@link StartupStep} as Flight Recorder events; because such events
|
||||
* only support base types, the {@link StartupStep.Tags} are serialized as a single String attribute.
|
||||
* <p>Once this is configured on the application context, you can record data by launching the application
|
||||
* with recording enabled: {@code java -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar app.jar}.
|
||||
* <p>This variant records {@link StartupStep} as Flight Recorder events. Because
|
||||
* such events only support base types, the
|
||||
* {@link org.springframework.core.metrics.StartupStep.Tags} are serialized as a
|
||||
* single String attribute.
|
||||
* <p>Once this is configured on the application context, you can record data by
|
||||
* launching the application with recording enabled:
|
||||
* {@code java -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar app.jar}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 5.3
|
||||
|
||||
@@ -644,7 +644,7 @@ public class AntPathMatcher implements PathMatcher {
|
||||
|
||||
private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?}|[^/{}]|\\\\[{}])+?)}");
|
||||
|
||||
private static final String DEFAULT_VARIABLE_PATTERN = "(.*)";
|
||||
private static final String DEFAULT_VARIABLE_PATTERN = "((?s).*)";
|
||||
|
||||
private final String rawPattern;
|
||||
|
||||
|
||||
@@ -457,7 +457,6 @@ public abstract class CollectionUtils {
|
||||
* @since 3.1
|
||||
*/
|
||||
public static <K, V> MultiValueMap<K, V> toMultiValueMap(Map<K, List<V>> targetMap) {
|
||||
Assert.notNull(targetMap, "'targetMap' must not be null");
|
||||
return new MultiValueMapAdapter<>(targetMap);
|
||||
}
|
||||
|
||||
|
||||
@@ -105,17 +105,15 @@ public class ConcurrentLruCache<K, V> {
|
||||
}
|
||||
// Generate value first, to prevent size inconsistency
|
||||
V value = this.generator.apply(key);
|
||||
int cacheSize = this.size;
|
||||
if (cacheSize == this.sizeLimit) {
|
||||
if (this.size == this.sizeLimit) {
|
||||
K leastUsed = this.queue.poll();
|
||||
if (leastUsed != null) {
|
||||
this.cache.remove(leastUsed);
|
||||
cacheSize--;
|
||||
}
|
||||
}
|
||||
this.queue.offer(key);
|
||||
this.cache.put(key, value);
|
||||
this.size = cacheSize + 1;
|
||||
this.size = this.cache.size();
|
||||
return value;
|
||||
}
|
||||
finally {
|
||||
@@ -123,6 +121,50 @@ public class ConcurrentLruCache<K, V> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given key is present in this cache.
|
||||
* @param key the key to check for
|
||||
* @return {@code true} if the key is present,
|
||||
* {@code false} if there was no matching key
|
||||
*/
|
||||
public boolean contains(K key) {
|
||||
return this.cache.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately remove the given key and any associated value.
|
||||
* @param key the key to evict the entry for
|
||||
* @return {@code true} if the key was present before,
|
||||
* {@code false} if there was no matching key
|
||||
*/
|
||||
public boolean remove(K key) {
|
||||
this.lock.writeLock().lock();
|
||||
try {
|
||||
boolean wasPresent = (this.cache.remove(key) != null);
|
||||
this.queue.remove(key);
|
||||
this.size = this.cache.size();
|
||||
return wasPresent;
|
||||
}
|
||||
finally {
|
||||
this.lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately remove all entries from this cache.
|
||||
*/
|
||||
public void clear() {
|
||||
this.lock.writeLock().lock();
|
||||
try {
|
||||
this.cache.clear();
|
||||
this.queue.clear();
|
||||
this.size = 0;
|
||||
}
|
||||
finally {
|
||||
this.lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current size of the cache.
|
||||
* @see #sizeLimit()
|
||||
|
||||
@@ -35,7 +35,8 @@ import java.util.Map;
|
||||
* @param <K> the key type
|
||||
* @param <V> the value element type
|
||||
*/
|
||||
public class LinkedMultiValueMap<K, V> extends MultiValueMapAdapter<K, V> implements Serializable, Cloneable {
|
||||
public class LinkedMultiValueMap<K, V> extends MultiValueMapAdapter<K, V> // new public base class in 5.3
|
||||
implements Serializable, Cloneable {
|
||||
|
||||
private static final long serialVersionUID = 3801124242820219131L;
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.BitSet;
|
||||
@@ -104,7 +106,7 @@ public class MimeType implements Comparable<MimeType>, Serializable {
|
||||
private final Map<String, String> parameters;
|
||||
|
||||
@Nullable
|
||||
private Charset resolvedCharset;
|
||||
private transient Charset resolvedCharset;
|
||||
|
||||
@Nullable
|
||||
private volatile String toStringValue;
|
||||
@@ -184,9 +186,9 @@ public class MimeType implements Comparable<MimeType>, Serializable {
|
||||
this.subtype = subtype.toLowerCase(Locale.ENGLISH);
|
||||
if (!CollectionUtils.isEmpty(parameters)) {
|
||||
Map<String, String> map = new LinkedCaseInsensitiveMap<>(parameters.size(), Locale.ENGLISH);
|
||||
parameters.forEach((attribute, value) -> {
|
||||
checkParameters(attribute, value);
|
||||
map.put(attribute, value);
|
||||
parameters.forEach((parameter, value) -> {
|
||||
checkParameters(parameter, value);
|
||||
map.put(parameter, value);
|
||||
});
|
||||
this.parameters = Collections.unmodifiableMap(map);
|
||||
}
|
||||
@@ -224,11 +226,11 @@ public class MimeType implements Comparable<MimeType>, Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkParameters(String attribute, String value) {
|
||||
Assert.hasLength(attribute, "'attribute' must not be empty");
|
||||
protected void checkParameters(String parameter, String value) {
|
||||
Assert.hasLength(parameter, "'parameter' must not be empty");
|
||||
Assert.hasLength(value, "'value' must not be empty");
|
||||
checkToken(attribute);
|
||||
if (PARAM_CHARSET.equals(attribute)) {
|
||||
checkToken(parameter);
|
||||
if (PARAM_CHARSET.equals(parameter)) {
|
||||
if (this.resolvedCharset == null) {
|
||||
this.resolvedCharset = Charset.forName(unquote(value));
|
||||
}
|
||||
@@ -591,6 +593,17 @@ public class MimeType implements Comparable<MimeType>, Serializable {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
// Rely on default serialization, just initialize state after deserialization.
|
||||
ois.defaultReadObject();
|
||||
|
||||
// Initialize transient fields.
|
||||
String charsetName = getParameter(PARAM_CHARSET);
|
||||
if (charsetName != null) {
|
||||
this.resolvedCharset = Charset.forName(unquote(charsetName));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the given String value into a {@code MimeType} object,
|
||||
|
||||
@@ -30,23 +30,30 @@ import org.springframework.lang.Nullable;
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @since 5.3
|
||||
* @param <K> the key type
|
||||
* @param <V> the value element type
|
||||
* @see CollectionUtils#toMultiValueMap
|
||||
* @see LinkedMultiValueMap
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializable {
|
||||
public class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializable {
|
||||
|
||||
private final Map<K, List<V>> targetMap;
|
||||
|
||||
|
||||
MultiValueMapAdapter(Map<K, List<V>> targetMap) {
|
||||
/**
|
||||
* Wrap the given target {@link Map} as a {@link MultiValueMap} adapter.
|
||||
* @param targetMap the plain target {@code Map}
|
||||
*/
|
||||
public MultiValueMapAdapter(Map<K, List<V>> targetMap) {
|
||||
Assert.notNull(targetMap, "'targetMap' must not be null");
|
||||
this.targetMap = targetMap;
|
||||
}
|
||||
|
||||
|
||||
// MultiValueMap implementation
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public V getFirst(K key) {
|
||||
@@ -96,6 +103,9 @@ class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializable {
|
||||
return singleValueMap;
|
||||
}
|
||||
|
||||
|
||||
// Map implementation
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.targetMap.size();
|
||||
|
||||
@@ -128,7 +128,6 @@ public abstract class ObjectUtils {
|
||||
* @see Optional#isPresent()
|
||||
* @see ObjectUtils#isEmpty(Object[])
|
||||
* @see StringUtils#hasLength(CharSequence)
|
||||
* @see StringUtils#isEmpty(Object)
|
||||
* @see CollectionUtils#isEmpty(java.util.Collection)
|
||||
* @see CollectionUtils#isEmpty(java.util.Map)
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
@@ -824,6 +824,19 @@ public abstract class ReflectionUtils {
|
||||
* @param method the method to check
|
||||
*/
|
||||
boolean matches(Method method);
|
||||
|
||||
/**
|
||||
* Create a composite filter based on this filter <em>and</em> the provided filter.
|
||||
* <p>If this filter does not match, the next filter will not be applied.
|
||||
* @param next the next {@code MethodFilter}
|
||||
* @return a composite {@code MethodFilter}
|
||||
* @throws IllegalArgumentException if the MethodFilter argument is {@code null}
|
||||
* @since 5.3.2
|
||||
*/
|
||||
default MethodFilter and(MethodFilter next) {
|
||||
Assert.notNull(next, "Next MethodFilter must not be null");
|
||||
return method -> matches(method) && next.matches(method);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -852,6 +865,19 @@ public abstract class ReflectionUtils {
|
||||
* @param field the field to check
|
||||
*/
|
||||
boolean matches(Field field);
|
||||
|
||||
/**
|
||||
* Create a composite filter based on this filter <em>and</em> the provided filter.
|
||||
* <p>If this filter does not match, the next filter will not be applied.
|
||||
* @param next the next {@code FieldFilter}
|
||||
* @return a composite {@code FieldFilter}
|
||||
* @throws IllegalArgumentException if the FieldFilter argument is {@code null}
|
||||
* @since 5.3.2
|
||||
*/
|
||||
default FieldFilter and(FieldFilter next) {
|
||||
Assert.notNull(next, "Next FieldFilter must not be null");
|
||||
return field -> matches(field) && next.matches(field);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -91,9 +91,10 @@ public abstract class StringUtils {
|
||||
* {@link #hasLength(String)} or {@link #hasText(String)} instead.</b>
|
||||
* @param str the candidate object (possibly a {@code String})
|
||||
* @since 3.2.1
|
||||
* @see #hasLength(String)
|
||||
* @see #hasText(String)
|
||||
* @deprecated as of 5.3, in favor of {@link #hasLength(String)} and
|
||||
* {@link #hasText(String)} (or {@link ObjectUtils#isEmpty(Object)})
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isEmpty(@Nullable Object str) {
|
||||
return (str == null || "".equals(str));
|
||||
}
|
||||
|
||||
@@ -161,9 +161,8 @@ class XMLEventStreamWriter implements XMLStreamWriter {
|
||||
public void writeEndElement() throws XMLStreamException {
|
||||
closeEmptyElementIfNecessary();
|
||||
int last = this.endElements.size() - 1;
|
||||
EndElement lastEndElement = this.endElements.get(last);
|
||||
EndElement lastEndElement = this.endElements.remove(last);
|
||||
this.eventWriter.add(lastEndElement);
|
||||
this.endElements.remove(last);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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.
|
||||
@@ -89,7 +89,7 @@ class MethodParameterTests {
|
||||
Method method = getClass().getMethod("method", String.class, Long.TYPE);
|
||||
MethodParameter methodParameter = new MethodParameter(method, 0);
|
||||
assertThat(methodParameter.hashCode()).isEqualTo(stringParameter.hashCode());
|
||||
assertThat(methodParameter.hashCode()).isNotEqualTo((long) longParameter.hashCode());
|
||||
assertThat(methodParameter.hashCode()).isNotEqualTo(longParameter.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+2
-1
@@ -367,8 +367,9 @@ class ReactiveAdapterRegistryTests {
|
||||
private static class ExtendedFlux<T> extends Flux<T> {
|
||||
|
||||
@Override
|
||||
public void subscribe(CoreSubscriber actual) {
|
||||
public void subscribe(CoreSubscriber<? super T> actual) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -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.
|
||||
@@ -86,7 +86,7 @@ class SynthesizingMethodParameterTests {
|
||||
Method method = getClass().getMethod("method", String.class, Long.TYPE);
|
||||
SynthesizingMethodParameter methodParameter = new SynthesizingMethodParameter(method, 0);
|
||||
assertThat(methodParameter.hashCode()).isEqualTo(stringParameter.hashCode());
|
||||
assertThat(methodParameter.hashCode()).isNotEqualTo((long) longParameter.hashCode());
|
||||
assertThat(methodParameter.hashCode()).isNotEqualTo(longParameter.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -139,20 +139,28 @@ class StringDecoderTests extends AbstractDecoderTests<StringDecoder> {
|
||||
@Test
|
||||
void maxInMemoryLimit() {
|
||||
Flux<DataBuffer> input = Flux.just(
|
||||
stringBuffer("abc\n"), stringBuffer("defg\n"), stringBuffer("hijkl\n"));
|
||||
stringBuffer("abc\n"), stringBuffer("defg\n"),
|
||||
stringBuffer("hi"), stringBuffer("jkl"), stringBuffer("mnop"));
|
||||
|
||||
this.decoder.setMaxInMemorySize(5);
|
||||
testDecode(input, String.class, step ->
|
||||
step.expectNext("abc", "defg").verifyError(DataBufferLimitException.class));
|
||||
}
|
||||
|
||||
@Test // gh-24312
|
||||
void maxInMemoryLimitReleaseUnprocessedLinesFromCurrentBuffer() {
|
||||
@Test
|
||||
void maxInMemoryLimitDoesNotApplyToParsedItemsThatDontRequireBuffering() {
|
||||
Flux<DataBuffer> input = Flux.just(
|
||||
stringBuffer("TOO MUCH DATA\nanother line\n\nand another\n"));
|
||||
|
||||
this.decoder.setMaxInMemorySize(5);
|
||||
testDecode(input, String.class, step -> step.verifyError(DataBufferLimitException.class));
|
||||
|
||||
testDecode(input, String.class, step -> step
|
||||
.expectNext("TOO MUCH DATA")
|
||||
.expectNext("another line")
|
||||
.expectNext("")
|
||||
.expectNext("and another")
|
||||
.expectComplete()
|
||||
.verify());
|
||||
}
|
||||
|
||||
@Test // gh-24339
|
||||
|
||||
+47
-9
@@ -35,6 +35,8 @@ import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.reactivestreams.Subscription;
|
||||
import reactor.core.publisher.BaseSubscriber;
|
||||
@@ -834,6 +836,22 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
|
||||
.verifyError(DataBufferLimitException.class);
|
||||
}
|
||||
|
||||
@Test // gh-26060
|
||||
void joinWithLimitDoesNotOverRelease() {
|
||||
NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(PooledByteBufAllocator.DEFAULT);
|
||||
byte[] bytes = "foo-bar-baz".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
NettyDataBuffer buffer = bufferFactory.allocateBuffer(bytes.length);
|
||||
buffer.getNativeBuffer().retain(); // should be at 2 now
|
||||
buffer.write(bytes);
|
||||
|
||||
Mono<DataBuffer> result = DataBufferUtils.join(Flux.just(buffer), 8);
|
||||
|
||||
StepVerifier.create(result).verifyError(DataBufferLimitException.class);
|
||||
assertThat(buffer.getNativeBuffer().refCnt()).isEqualTo(1);
|
||||
buffer.release();
|
||||
}
|
||||
|
||||
@ParameterizedDataBufferAllocatingTest
|
||||
void joinErrors(String displayName, DataBufferFactory bufferFactory) {
|
||||
super.bufferFactory = bufferFactory;
|
||||
@@ -886,18 +904,38 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
|
||||
void matcher2(String displayName, DataBufferFactory bufferFactory) {
|
||||
super.bufferFactory = bufferFactory;
|
||||
|
||||
DataBuffer foo = stringBuffer("fooobar");
|
||||
DataBuffer foo = stringBuffer("foooobar");
|
||||
|
||||
byte[] delims = "oo".getBytes(StandardCharsets.UTF_8);
|
||||
DataBufferUtils.Matcher matcher = DataBufferUtils.matcher(delims);
|
||||
int result = matcher.match(foo);
|
||||
assertThat(result).isEqualTo(2);
|
||||
foo.readPosition(2);
|
||||
result = matcher.match(foo);
|
||||
assertThat(result).isEqualTo(3);
|
||||
foo.readPosition(3);
|
||||
result = matcher.match(foo);
|
||||
assertThat(result).isEqualTo(-1);
|
||||
int endIndex = matcher.match(foo);
|
||||
assertThat(endIndex).isEqualTo(2);
|
||||
foo.readPosition(endIndex + 1);
|
||||
endIndex = matcher.match(foo);
|
||||
assertThat(endIndex).isEqualTo(4);
|
||||
foo.readPosition(endIndex + 1);
|
||||
endIndex = matcher.match(foo);
|
||||
assertThat(endIndex).isEqualTo(-1);
|
||||
|
||||
release(foo);
|
||||
}
|
||||
|
||||
@ParameterizedDataBufferAllocatingTest
|
||||
void matcher3(String displayName, DataBufferFactory bufferFactory) {
|
||||
super.bufferFactory = bufferFactory;
|
||||
|
||||
DataBuffer foo = stringBuffer("foooobar");
|
||||
|
||||
byte[] delims = "oo".getBytes(StandardCharsets.UTF_8);
|
||||
DataBufferUtils.Matcher matcher = DataBufferUtils.matcher(delims);
|
||||
int endIndex = matcher.match(foo);
|
||||
assertThat(endIndex).isEqualTo(2);
|
||||
foo.readPosition(endIndex + 1);
|
||||
endIndex = matcher.match(foo);
|
||||
assertThat(endIndex).isEqualTo(4);
|
||||
foo.readPosition(endIndex + 1);
|
||||
endIndex = matcher.match(foo);
|
||||
assertThat(endIndex).isEqualTo(-1);
|
||||
|
||||
release(foo);
|
||||
}
|
||||
|
||||
+7
-3
@@ -17,9 +17,11 @@ package org.springframework.core.io.buffer;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link LimitedDataBufferList}.
|
||||
* @author Rossen Stoyanchev
|
||||
@@ -29,8 +31,10 @@ public class LimitedDataBufferListTests {
|
||||
|
||||
@Test
|
||||
void limitEnforced() {
|
||||
Assertions.assertThatThrownBy(() -> new LimitedDataBufferList(5).add(toDataBuffer("123456")))
|
||||
.isInstanceOf(DataBufferLimitException.class);
|
||||
LimitedDataBufferList list = new LimitedDataBufferList(5);
|
||||
|
||||
assertThatThrownBy(() -> list.add(toDataBuffer("123456"))).isInstanceOf(DataBufferLimitException.class);
|
||||
assertThat(list).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -130,6 +130,7 @@ class AntPathMatcherTests {
|
||||
assertThat(pathMatcher.match("", "")).isTrue();
|
||||
|
||||
assertThat(pathMatcher.match("/{bla}.*", "/testing.html")).isTrue();
|
||||
assertThat(pathMatcher.match("/{bla}", "//x\ny")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.util;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class ConcurrentLruCacheTests {
|
||||
|
||||
private final ConcurrentLruCache<String, String> cache = new ConcurrentLruCache<>(2, key -> key + "value");
|
||||
|
||||
|
||||
@Test
|
||||
void getAndSize() {
|
||||
assertThat(this.cache.sizeLimit()).isEqualTo(2);
|
||||
assertThat(this.cache.size()).isEqualTo(0);
|
||||
assertThat(this.cache.get("k1")).isEqualTo("k1value");
|
||||
assertThat(this.cache.size()).isEqualTo(1);
|
||||
assertThat(this.cache.contains("k1")).isTrue();
|
||||
assertThat(this.cache.get("k2")).isEqualTo("k2value");
|
||||
assertThat(this.cache.size()).isEqualTo(2);
|
||||
assertThat(this.cache.contains("k1")).isTrue();
|
||||
assertThat(this.cache.contains("k2")).isTrue();
|
||||
assertThat(this.cache.get("k3")).isEqualTo("k3value");
|
||||
assertThat(this.cache.size()).isEqualTo(2);
|
||||
assertThat(this.cache.contains("k1")).isFalse();
|
||||
assertThat(this.cache.contains("k2")).isTrue();
|
||||
assertThat(this.cache.contains("k3")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeAndSize() {
|
||||
assertThat(this.cache.get("k1")).isEqualTo("k1value");
|
||||
assertThat(this.cache.get("k2")).isEqualTo("k2value");
|
||||
assertThat(this.cache.size()).isEqualTo(2);
|
||||
assertThat(this.cache.contains("k1")).isTrue();
|
||||
assertThat(this.cache.contains("k2")).isTrue();
|
||||
this.cache.remove("k2");
|
||||
assertThat(this.cache.size()).isEqualTo(1);
|
||||
assertThat(this.cache.contains("k1")).isTrue();
|
||||
assertThat(this.cache.contains("k2")).isFalse();
|
||||
assertThat(this.cache.get("k3")).isEqualTo("k3value");
|
||||
assertThat(this.cache.size()).isEqualTo(2);
|
||||
assertThat(this.cache.contains("k1")).isTrue();
|
||||
assertThat(this.cache.contains("k2")).isFalse();
|
||||
assertThat(this.cache.contains("k3")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void clearAndSize() {
|
||||
assertThat(this.cache.get("k1")).isEqualTo("k1value");
|
||||
assertThat(this.cache.get("k2")).isEqualTo("k2value");
|
||||
assertThat(this.cache.size()).isEqualTo(2);
|
||||
assertThat(this.cache.contains("k1")).isTrue();
|
||||
assertThat(this.cache.contains("k2")).isTrue();
|
||||
this.cache.clear();
|
||||
assertThat(this.cache.size()).isEqualTo(0);
|
||||
assertThat(this.cache.contains("k1")).isFalse();
|
||||
assertThat(this.cache.contains("k2")).isFalse();
|
||||
assertThat(this.cache.get("k3")).isEqualTo("k3value");
|
||||
assertThat(this.cache.size()).isEqualTo(1);
|
||||
assertThat(this.cache.contains("k1")).isFalse();
|
||||
assertThat(this.cache.contains("k2")).isFalse();
|
||||
assertThat(this.cache.contains("k3")).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.testfixture.io.SerializationTestUtils;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -267,13 +268,13 @@ class MimeTypeTests {
|
||||
assertThat(mimeType.getParameter("attr")).isEqualTo("'v>alue'");
|
||||
}
|
||||
|
||||
@Test // SPR-16630
|
||||
@Test // SPR-16630
|
||||
void parseMimeTypeWithSpacesAroundEquals() {
|
||||
MimeType mimeType = MimeTypeUtils.parseMimeType("multipart/x-mixed-replace;boundary = --myboundary");
|
||||
assertThat(mimeType.getParameter("boundary")).isEqualTo("--myboundary");
|
||||
}
|
||||
|
||||
@Test // SPR-16630
|
||||
@Test // SPR-16630
|
||||
void parseMimeTypeWithSpacesAroundEqualsAndQuotedValue() {
|
||||
MimeType mimeType = MimeTypeUtils.parseMimeType("text/plain; foo = \" bar \" ");
|
||||
assertThat(mimeType.getParameter("foo")).isEqualTo("\" bar \"");
|
||||
@@ -303,14 +304,14 @@ class MimeTypeTests {
|
||||
assertThat(mimeTypes.size()).as("Invalid amount of mime types").isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test // gh-23241
|
||||
@Test // gh-23241
|
||||
void parseMimeTypesWithTrailingComma() {
|
||||
List<MimeType> mimeTypes = MimeTypeUtils.parseMimeTypes("text/plain, text/html,");
|
||||
assertThat(mimeTypes).as("No mime types returned").isNotNull();
|
||||
assertThat(mimeTypes.size()).as("Incorrect number of mime types").isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test // SPR-17459
|
||||
@Test // SPR-17459
|
||||
void parseMimeTypesWithQuotedParameters() {
|
||||
testWithQuotedParameters("foo/bar;param=\",\"");
|
||||
testWithQuotedParameters("foo/bar;param=\"s,a,\"");
|
||||
@@ -332,7 +333,7 @@ class MimeTypeTests {
|
||||
assertThat(type.getSubtypeSuffix()).isEqualTo("json");
|
||||
}
|
||||
|
||||
@Test // gh-25350
|
||||
@Test // gh-25350
|
||||
void wildcardSubtypeCompatibleWithSuffix() {
|
||||
MimeType applicationStar = new MimeType("application", "*");
|
||||
MimeType applicationVndJson = new MimeType("application", "vnd.something+json");
|
||||
@@ -342,8 +343,9 @@ class MimeTypeTests {
|
||||
private void testWithQuotedParameters(String... mimeTypes) {
|
||||
String s = String.join(",", mimeTypes);
|
||||
List<MimeType> actual = MimeTypeUtils.parseMimeTypes(s);
|
||||
|
||||
assertThat(actual.size()).isEqualTo(mimeTypes.length);
|
||||
for (int i=0; i < mimeTypes.length; i++) {
|
||||
for (int i = 0; i < mimeTypes.length; i++) {
|
||||
assertThat(actual.get(i).toString()).isEqualTo(mimeTypes[i]);
|
||||
}
|
||||
}
|
||||
@@ -370,6 +372,7 @@ class MimeTypeTests {
|
||||
|
||||
List<MimeType> result = new ArrayList<>(expected);
|
||||
Random rnd = new Random();
|
||||
|
||||
// shuffle & sort 10 times
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Collections.shuffle(result, rnd);
|
||||
@@ -399,11 +402,7 @@ class MimeTypeTests {
|
||||
assertThat(m2.compareTo(m1) != 0).as("Invalid comparison result").isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* SPR-13157
|
||||
* @since 4.2
|
||||
*/
|
||||
@Test
|
||||
@Test // SPR-13157
|
||||
void equalsIsCaseInsensitiveForCharsets() {
|
||||
MimeType m1 = new MimeType("text", "plain", singletonMap("charset", "UTF-8"));
|
||||
MimeType m2 = new MimeType("text", "plain", singletonMap("charset", "utf-8"));
|
||||
@@ -413,4 +412,12 @@ class MimeTypeTests {
|
||||
assertThat(m2.compareTo(m1)).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test // gh-26127
|
||||
void serialize() throws Exception {
|
||||
MimeType original = new MimeType("text", "plain", StandardCharsets.UTF_8);
|
||||
MimeType deserialized = SerializationTestUtils.serializeAndDeserialize(original);
|
||||
assertThat(deserialized).isEqualTo(original);
|
||||
assertThat(original).isEqualTo(deserialized);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -186,12 +186,7 @@ class ReflectionUtilsTests {
|
||||
@Test
|
||||
void doWithProtectedMethods() {
|
||||
ListSavingMethodCallback mc = new ListSavingMethodCallback();
|
||||
ReflectionUtils.doWithMethods(TestObject.class, mc, new ReflectionUtils.MethodFilter() {
|
||||
@Override
|
||||
public boolean matches(Method m) {
|
||||
return Modifier.isProtected(m.getModifiers());
|
||||
}
|
||||
});
|
||||
ReflectionUtils.doWithMethods(TestObject.class, mc, method -> Modifier.isProtected(method.getModifiers()));
|
||||
assertThat(mc.getMethodNames().isEmpty()).isFalse();
|
||||
assertThat(mc.getMethodNames().contains("clone")).as("Must find protected method on Object").isTrue();
|
||||
assertThat(mc.getMethodNames().contains("finalize")).as("Must find protected method on Object").isTrue();
|
||||
|
||||
+10
-9
@@ -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.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.core.testfixture.io.buffer;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.DataBufferWrapper;
|
||||
import org.springframework.core.io.buffer.PooledDataBuffer;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -67,19 +68,19 @@ class LeakAwareDataBuffer extends DataBufferWrapper implements PooledDataBuffer
|
||||
|
||||
@Override
|
||||
public PooledDataBuffer retain() {
|
||||
DataBuffer delegate = dataBuffer();
|
||||
if (delegate instanceof PooledDataBuffer) {
|
||||
((PooledDataBuffer) delegate).retain();
|
||||
}
|
||||
DataBufferUtils.retain(dataBuffer());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledDataBuffer touch(Object hint) {
|
||||
DataBufferUtils.touch(dataBuffer(), hint);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean release() {
|
||||
DataBuffer delegate = dataBuffer();
|
||||
if (delegate instanceof PooledDataBuffer) {
|
||||
((PooledDataBuffer) delegate).release();
|
||||
}
|
||||
DataBufferUtils.release(dataBuffer());
|
||||
return isAllocated();
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -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.
|
||||
@@ -54,10 +54,9 @@ public abstract class AstUtils {
|
||||
}
|
||||
else {
|
||||
if (targetType != null) {
|
||||
int pos = 0;
|
||||
for (Class<?> clazz : targets) {
|
||||
if (clazz == targetType) { // put exact matches on the front to be tried first?
|
||||
specificAccessors.add(pos++, resolver);
|
||||
specificAccessors.add(resolver);
|
||||
}
|
||||
else if (clazz.isAssignableFrom(targetType)) { // put supertype matches at the end of the
|
||||
// specificAccessor list
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Represents the elvis operator ?:. For an expression "a?:b" if a is not null, the value
|
||||
@@ -52,7 +51,7 @@ public class Elvis extends SpelNodeImpl {
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
TypedValue value = this.children[0].getValueInternal(state);
|
||||
// If this check is changed, the generateCode method will need changing too
|
||||
if (!StringUtils.isEmpty(value.getValue())) {
|
||||
if (value.getValue() != null && !"".equals(value.getValue())) {
|
||||
return value;
|
||||
}
|
||||
else {
|
||||
|
||||
+55
-30
@@ -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.
|
||||
@@ -63,34 +63,36 @@ import org.springframework.util.StringUtils;
|
||||
* <p>Individual expressions can be compiled by calling {@code SpelCompiler.compile(expression)}.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.1
|
||||
*/
|
||||
public final class SpelCompiler implements Opcodes {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SpelCompiler.class);
|
||||
|
||||
private static final int CLASSES_DEFINED_LIMIT = 100;
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SpelCompiler.class);
|
||||
|
||||
// A compiler is created for each classloader, it manages a child class loader of that
|
||||
// classloader and the child is used to load the compiled expressions.
|
||||
private static final Map<ClassLoader, SpelCompiler> compilers = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
|
||||
// The child ClassLoader used to load the compiled expression classes
|
||||
private ChildClassLoader ccl;
|
||||
private volatile ChildClassLoader childClassLoader;
|
||||
|
||||
// Counter suffix for generated classes within this SpelCompiler instance
|
||||
private final AtomicInteger suffixId = new AtomicInteger(1);
|
||||
|
||||
|
||||
private SpelCompiler(@Nullable ClassLoader classloader) {
|
||||
this.ccl = new ChildClassLoader(classloader);
|
||||
this.childClassLoader = new ChildClassLoader(classloader);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempt compilation of the supplied expression. A check is made to see
|
||||
* if it is compilable before compilation proceeds. The check involves
|
||||
* visiting all the nodes in the expression Ast and ensuring enough state
|
||||
* visiting all the nodes in the expression AST and ensuring enough state
|
||||
* is known about them that bytecode can be generated for them.
|
||||
* @param expression the expression to compile
|
||||
* @return an instance of the class implementing the compiled expression,
|
||||
@@ -125,7 +127,7 @@ public final class SpelCompiler implements Opcodes {
|
||||
|
||||
/**
|
||||
* Generate the class that encapsulates the compiled expression and define it.
|
||||
* The generated class will be a subtype of CompiledExpression.
|
||||
* The generated class will be a subtype of CompiledExpression.
|
||||
* @param expressionToCompile the expression to be compiled
|
||||
* @return the expression call, or {@code null} if the decision was to opt out of
|
||||
* compilation during code generation
|
||||
@@ -135,7 +137,7 @@ public final class SpelCompiler implements Opcodes {
|
||||
// Create class outline 'spel/ExNNN extends org.springframework.expression.spel.CompiledExpression'
|
||||
String className = "spel/Ex" + getNextSuffix();
|
||||
ClassWriter cw = new ExpressionClassWriter();
|
||||
cw.visit(V1_5, ACC_PUBLIC, className, null, "org/springframework/expression/spel/CompiledExpression", null);
|
||||
cw.visit(V1_8, ACC_PUBLIC, className, null, "org/springframework/expression/spel/CompiledExpression", null);
|
||||
|
||||
// Create default constructor
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
@@ -150,7 +152,7 @@ public final class SpelCompiler implements Opcodes {
|
||||
// Create getValue() method
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "getValue",
|
||||
"(Ljava/lang/Object;Lorg/springframework/expression/EvaluationContext;)Ljava/lang/Object;", null,
|
||||
new String[ ]{"org/springframework/expression/EvaluationException"});
|
||||
new String[] {"org/springframework/expression/EvaluationException"});
|
||||
mv.visitCode();
|
||||
|
||||
CodeFlow cf = new CodeFlow(className, cw);
|
||||
@@ -187,7 +189,7 @@ public final class SpelCompiler implements Opcodes {
|
||||
|
||||
/**
|
||||
* Load a compiled expression class. Makes sure the classloaders aren't used too much
|
||||
* because they anchor compiled classes in memory and prevent GC. If you have expressions
|
||||
* because they anchor compiled classes in memory and prevent GC. If you have expressions
|
||||
* continually recompiling over time then by replacing the classloader periodically
|
||||
* at least some of the older variants can be garbage collected.
|
||||
* @param name the name of the class
|
||||
@@ -196,12 +198,25 @@ public final class SpelCompiler implements Opcodes {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<? extends CompiledExpression> loadClass(String name, byte[] bytes) {
|
||||
if (this.ccl.getClassesDefinedCount() > CLASSES_DEFINED_LIMIT) {
|
||||
this.ccl = new ChildClassLoader(this.ccl.getParent());
|
||||
ChildClassLoader ccl = this.childClassLoader;
|
||||
if (ccl.getClassesDefinedCount() >= CLASSES_DEFINED_LIMIT) {
|
||||
synchronized (this) {
|
||||
ChildClassLoader currentCcl = this.childClassLoader;
|
||||
if (ccl == currentCcl) {
|
||||
// Still the same ClassLoader that needs to be replaced...
|
||||
ccl = new ChildClassLoader(ccl.getParent());
|
||||
this.childClassLoader = ccl;
|
||||
}
|
||||
else {
|
||||
// Already replaced by some other thread, let's pick it up.
|
||||
ccl = currentCcl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (Class<? extends CompiledExpression>) this.ccl.defineClass(name, bytes);
|
||||
return (Class<? extends CompiledExpression>) ccl.defineClass(name, bytes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Factory method for compiler instances. The returned SpelCompiler will
|
||||
* attach a class loader as the child of the given class loader and this
|
||||
@@ -211,21 +226,28 @@ public final class SpelCompiler implements Opcodes {
|
||||
*/
|
||||
public static SpelCompiler getCompiler(@Nullable ClassLoader classLoader) {
|
||||
ClassLoader clToUse = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
|
||||
synchronized (compilers) {
|
||||
SpelCompiler compiler = compilers.get(clToUse);
|
||||
if (compiler == null) {
|
||||
compiler = new SpelCompiler(clToUse);
|
||||
compilers.put(clToUse, compiler);
|
||||
// Quick check for existing compiler without lock contention
|
||||
SpelCompiler compiler = compilers.get(clToUse);
|
||||
if (compiler == null) {
|
||||
// Full lock now since we're creating a child ClassLoader
|
||||
synchronized (compilers) {
|
||||
compiler = compilers.get(clToUse);
|
||||
if (compiler == null) {
|
||||
compiler = new SpelCompiler(clToUse);
|
||||
compilers.put(clToUse, compiler);
|
||||
}
|
||||
}
|
||||
return compiler;
|
||||
}
|
||||
return compiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request that an attempt is made to compile the specified expression. It may fail if
|
||||
* components of the expression are not suitable for compilation or the data types
|
||||
* involved are not suitable for compilation. Used for testing.
|
||||
* @return true if the expression was successfully compiled
|
||||
* Request that an attempt is made to compile the specified expression.
|
||||
* It may fail if components of the expression are not suitable for compilation
|
||||
* or the data types involved are not suitable for compilation. Used for testing.
|
||||
* @param expression the expression to compile
|
||||
* @return {@code true} if the expression was successfully compiled,
|
||||
* {@code false} otherwise
|
||||
*/
|
||||
public static boolean compile(Expression expression) {
|
||||
return (expression instanceof SpelExpression && ((SpelExpression) expression).compileExpression());
|
||||
@@ -250,24 +272,27 @@ public final class SpelCompiler implements Opcodes {
|
||||
|
||||
private static final URL[] NO_URLS = new URL[0];
|
||||
|
||||
private int classesDefinedCount = 0;
|
||||
private final AtomicInteger classesDefinedCount = new AtomicInteger(0);
|
||||
|
||||
public ChildClassLoader(@Nullable ClassLoader classLoader) {
|
||||
super(NO_URLS, classLoader);
|
||||
}
|
||||
|
||||
int getClassesDefinedCount() {
|
||||
return this.classesDefinedCount;
|
||||
}
|
||||
|
||||
public Class<?> defineClass(String name, byte[] bytes) {
|
||||
Class<?> clazz = super.defineClass(name, bytes, 0, bytes.length);
|
||||
this.classesDefinedCount++;
|
||||
this.classesDefinedCount.incrementAndGet();
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public int getClassesDefinedCount() {
|
||||
return this.classesDefinedCount.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An ASM ClassWriter extension bound to the SpelCompiler's ClassLoader.
|
||||
*/
|
||||
private class ExpressionClassWriter extends ClassWriter {
|
||||
|
||||
public ExpressionClassWriter() {
|
||||
@@ -276,7 +301,7 @@ public final class SpelCompiler implements Opcodes {
|
||||
|
||||
@Override
|
||||
protected ClassLoader getClassLoader() {
|
||||
return ccl;
|
||||
return childClassLoader;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-10
@@ -47,7 +47,7 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A powerful {@link PropertyAccessor} that uses reflection to access properties
|
||||
* for reading and possibly also for writing.
|
||||
* for reading and possibly also for writing on a target instance.
|
||||
*
|
||||
* <p>A property can be referenced through a public getter method (when being read)
|
||||
* or a public setter method (when being written), and also as a public field.
|
||||
@@ -98,8 +98,8 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new property accessor for reading and possibly writing.
|
||||
* @param allowWrite whether to also allow for write operations
|
||||
* Create a new property accessor for reading and possibly also writing.
|
||||
* @param allowWrite whether to allow write operations on a target instance
|
||||
* @since 4.3.15
|
||||
* @see #canWrite
|
||||
*/
|
||||
@@ -395,6 +395,11 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
if (method == null) {
|
||||
method = findMethodForProperty(getPropertyMethodSuffixes(propertyName),
|
||||
"is", clazz, mustBeStatic, 0, BOOLEAN_TYPES);
|
||||
if (method == null) {
|
||||
// Record-style plain accessor method, e.g. name()
|
||||
method = findMethodForProperty(new String[] {propertyName},
|
||||
"", clazz, mustBeStatic, 0, ANY_TYPES);
|
||||
}
|
||||
}
|
||||
return method;
|
||||
}
|
||||
@@ -623,8 +628,8 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CacheKey [clazz=" + this.clazz.getName() + ", property=" + this.property + ", " +
|
||||
this.property + ", targetIsClass=" + this.targetIsClass + "]";
|
||||
return "PropertyCacheKey [clazz=" + this.clazz.getName() + ", property=" + this.property +
|
||||
", targetIsClass=" + this.targetIsClass + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -683,12 +688,11 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
return true;
|
||||
}
|
||||
getterName = "is" + StringUtils.capitalize(name);
|
||||
return getterName.equals(method.getName());
|
||||
}
|
||||
else {
|
||||
Field field = (Field) this.member;
|
||||
return field.getName().equals(name);
|
||||
if (getterName.equals(method.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return this.member.getName().equals(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user