mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
128 Commits
| 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 |
+26
-26
@@ -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,18 +25,18 @@ configure(allprojects) { project ->
|
||||
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom "com.fasterxml.jackson:jackson-bom:2.11.3"
|
||||
mavenBom "io.netty:netty-bom:4.1.53.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.0"
|
||||
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.34.v20201102"
|
||||
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.4.10"
|
||||
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.4.1"
|
||||
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-jul'
|
||||
@@ -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,7 +64,7 @@ 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.reactivex.rxjava3:rxjava:3.0.8"
|
||||
dependency "io.projectreactor.tools:blockhound:1.0.4.RELEASE"
|
||||
|
||||
dependency "com.caucho:hessian:4.0.63"
|
||||
@@ -73,9 +73,9 @@ configure(allprojects) { project ->
|
||||
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"
|
||||
}
|
||||
@@ -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.23.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.39') {
|
||||
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.39') {
|
||||
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,14 +154,14 @@ 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") {
|
||||
exclude group: "commons-logging", name: "commons-logging"
|
||||
}
|
||||
dependency 'org.apache.httpcomponents.client5:httpclient5:5.0.3'
|
||||
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.0.2'
|
||||
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.0.3'
|
||||
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.4"
|
||||
|
||||
dependency "org.jruby:jruby:9.2.13.0"
|
||||
@@ -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.18.0"
|
||||
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.6.0') {
|
||||
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") {
|
||||
@@ -339,7 +339,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "8.36.2"
|
||||
toolVersion = "8.38"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -37,17 +37,17 @@ anchors:
|
||||
|
||||
resource_types:
|
||||
- name: artifactory-resource
|
||||
type: docker-image
|
||||
type: registry-image
|
||||
source:
|
||||
repository: springio/artifactory-resource
|
||||
tag: 0.0.12
|
||||
- name: github-status-resource
|
||||
type: docker-image
|
||||
type: registry-image
|
||||
source:
|
||||
repository: dpb587/github-status-resource
|
||||
tag: master
|
||||
- name: slack-notification
|
||||
type: docker-image
|
||||
type: registry-image
|
||||
source:
|
||||
repository: cfcommunity/slack-notification-resource
|
||||
tag: latest
|
||||
|
||||
@@ -6,11 +6,11 @@ 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 \
|
||||
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
|
||||
distribute $RELEASE_TYPE $BUILD_INFO_LOCATION > /dev/null || { exit 1; }
|
||||
distribute $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
|
||||
|
||||
echo "Promotion complete"
|
||||
echo $version > version/version
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.3.1-SNAPSHOT
|
||||
version=5.3.2
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
+2
-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 {
|
||||
@@ -113,7 +113,7 @@ dokka {
|
||||
}
|
||||
|
||||
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.7-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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
+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")
|
||||
|
||||
+1
-2
@@ -141,8 +141,7 @@ 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) {
|
||||
|
||||
+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
-22
@@ -785,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
|
||||
@@ -1838,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
|
||||
@@ -2388,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
|
||||
@@ -2403,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
|
||||
@@ -2419,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
|
||||
@@ -2542,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);
|
||||
@@ -2559,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
|
||||
@@ -2596,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-5
@@ -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()) {
|
||||
|
||||
-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();
|
||||
|
||||
+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() }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+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);
|
||||
|
||||
@@ -487,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}.
|
||||
|
||||
+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 [" +
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+18
@@ -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;
|
||||
|
||||
+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
|
||||
|
||||
@@ -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
|
||||
|
||||
+5
-5
@@ -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
|
||||
*/
|
||||
@@ -628,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
|
||||
|
||||
+34
-36
@@ -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,10 +36,10 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class SpelParserTests {
|
||||
class SpelParserTests {
|
||||
|
||||
@Test
|
||||
public void theMostBasic() {
|
||||
void theMostBasic() {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
SpelExpression expr = parser.parseRaw("2");
|
||||
assertThat(expr).isNotNull();
|
||||
@@ -50,7 +50,7 @@ public class SpelParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void valueType() {
|
||||
void valueType() {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
EvaluationContext ctx = new StandardEvaluationContext();
|
||||
Class<?> c = parser.parseRaw("2").getValueType();
|
||||
@@ -66,7 +66,7 @@ public class SpelParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whitespace() {
|
||||
void whitespace() {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
SpelExpression expr = parser.parseRaw("2 + 3");
|
||||
assertThat(expr.getValue()).isEqualTo(5);
|
||||
@@ -79,7 +79,7 @@ public class SpelParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arithmeticPlus1() {
|
||||
void arithmeticPlus1() {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
SpelExpression expr = parser.parseRaw("2+2");
|
||||
assertThat(expr).isNotNull();
|
||||
@@ -88,66 +88,65 @@ public class SpelParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arithmeticPlus2() {
|
||||
void arithmeticPlus2() {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
SpelExpression expr = parser.parseRaw("37+41");
|
||||
assertThat(expr.getValue()).isEqualTo(78);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arithmeticMultiply1() {
|
||||
void arithmeticMultiply1() {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
SpelExpression expr = parser.parseRaw("2*3");
|
||||
assertThat(expr).isNotNull();
|
||||
assertThat(expr.getAST()).isNotNull();
|
||||
// printAst(expr.getAST(),0);
|
||||
assertThat(expr.getValue()).isEqualTo(6);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arithmeticPrecedence1() {
|
||||
void arithmeticPrecedence1() {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
SpelExpression expr = parser.parseRaw("2*3+5");
|
||||
assertThat(expr.getValue()).isEqualTo(11);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generalExpressions() {
|
||||
void generalExpressions() {
|
||||
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
parser.parseRaw("new String");
|
||||
})
|
||||
.satisfies(ex -> parseExceptionRequirements(SpelMessage.MISSING_CONSTRUCTOR_ARGS, 10));
|
||||
.satisfies(parseExceptionRequirements(SpelMessage.MISSING_CONSTRUCTOR_ARGS, 10));
|
||||
|
||||
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
parser.parseRaw("new String(3,");
|
||||
})
|
||||
.satisfies(ex -> parseExceptionRequirements(SpelMessage.RUN_OUT_OF_ARGUMENTS, 10));
|
||||
.satisfies(parseExceptionRequirements(SpelMessage.RUN_OUT_OF_ARGUMENTS, 10));
|
||||
|
||||
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
parser.parseRaw("new String(3");
|
||||
})
|
||||
.satisfies(ex -> parseExceptionRequirements(SpelMessage.RUN_OUT_OF_ARGUMENTS, 10));
|
||||
.satisfies(parseExceptionRequirements(SpelMessage.RUN_OUT_OF_ARGUMENTS, 10));
|
||||
|
||||
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
parser.parseRaw("new String(");
|
||||
})
|
||||
.satisfies(ex -> parseExceptionRequirements(SpelMessage.RUN_OUT_OF_ARGUMENTS, 10));
|
||||
.satisfies(parseExceptionRequirements(SpelMessage.RUN_OUT_OF_ARGUMENTS, 10));
|
||||
|
||||
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
parser.parseRaw("\"abc");
|
||||
})
|
||||
.satisfies(ex -> parseExceptionRequirements(SpelMessage.NON_TERMINATING_DOUBLE_QUOTED_STRING, 0));
|
||||
.satisfies(parseExceptionRequirements(SpelMessage.NON_TERMINATING_DOUBLE_QUOTED_STRING, 0));
|
||||
|
||||
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
parser.parseRaw("'abc");
|
||||
})
|
||||
.satisfies(ex -> parseExceptionRequirements(SpelMessage.NON_TERMINATING_QUOTED_STRING, 0));
|
||||
.satisfies(parseExceptionRequirements(SpelMessage.NON_TERMINATING_QUOTED_STRING, 0));
|
||||
|
||||
}
|
||||
|
||||
@@ -161,38 +160,38 @@ public class SpelParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arithmeticPrecedence2() {
|
||||
void arithmeticPrecedence2() {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
SpelExpression expr = parser.parseRaw("2+3*5");
|
||||
assertThat(expr.getValue()).isEqualTo(17);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arithmeticPrecedence3() {
|
||||
void arithmeticPrecedence3() {
|
||||
SpelExpression expr = new SpelExpressionParser().parseRaw("3+10/2");
|
||||
assertThat(expr.getValue()).isEqualTo(8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arithmeticPrecedence4() {
|
||||
void arithmeticPrecedence4() {
|
||||
SpelExpression expr = new SpelExpressionParser().parseRaw("10/2+3");
|
||||
assertThat(expr.getValue()).isEqualTo(8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arithmeticPrecedence5() {
|
||||
void arithmeticPrecedence5() {
|
||||
SpelExpression expr = new SpelExpressionParser().parseRaw("(4+10)/2");
|
||||
assertThat(expr.getValue()).isEqualTo(7);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arithmeticPrecedence6() {
|
||||
void arithmeticPrecedence6() {
|
||||
SpelExpression expr = new SpelExpressionParser().parseRaw("(3+2)*2");
|
||||
assertThat(expr.getValue()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void booleanOperators() {
|
||||
void booleanOperators() {
|
||||
SpelExpression expr = new SpelExpressionParser().parseRaw("true");
|
||||
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.TRUE);
|
||||
expr = new SpelExpressionParser().parseRaw("false");
|
||||
@@ -210,7 +209,7 @@ public class SpelParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void booleanOperators_symbolic_spr9614() {
|
||||
void booleanOperators_symbolic_spr9614() {
|
||||
SpelExpression expr = new SpelExpressionParser().parseRaw("true");
|
||||
assertThat(expr.getValue(Boolean.class)).isEqualTo(Boolean.TRUE);
|
||||
expr = new SpelExpressionParser().parseRaw("false");
|
||||
@@ -228,7 +227,7 @@ public class SpelParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stringLiterals() {
|
||||
void stringLiterals() {
|
||||
SpelExpression expr = new SpelExpressionParser().parseRaw("'howdy'");
|
||||
assertThat(expr.getValue()).isEqualTo("howdy");
|
||||
expr = new SpelExpressionParser().parseRaw("'hello '' world'");
|
||||
@@ -236,13 +235,13 @@ public class SpelParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stringLiterals2() {
|
||||
void stringLiterals2() {
|
||||
SpelExpression expr = new SpelExpressionParser().parseRaw("'howdy'.substring(0,2)");
|
||||
assertThat(expr.getValue()).isEqualTo("ho");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringLiterals_DoubleQuotes_spr9620() {
|
||||
void testStringLiterals_DoubleQuotes_spr9620() {
|
||||
SpelExpression expr = new SpelExpressionParser().parseRaw("\"double quote: \"\".\"");
|
||||
assertThat(expr.getValue()).isEqualTo("double quote: \".");
|
||||
expr = new SpelExpressionParser().parseRaw("\"hello \"\" world\"");
|
||||
@@ -250,7 +249,7 @@ public class SpelParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringLiterals_DoubleQuotes_spr9620_2() {
|
||||
void testStringLiterals_DoubleQuotes_spr9620_2() {
|
||||
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() ->
|
||||
new SpelExpressionParser().parseRaw("\"double quote: \\\"\\\".\""))
|
||||
.satisfies(ex -> {
|
||||
@@ -260,7 +259,7 @@ public class SpelParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionalInformation() {
|
||||
void positionalInformation() {
|
||||
SpelExpression expr = new SpelExpressionParser().parseRaw("true and true or false");
|
||||
SpelNode rootAst = expr.getAST();
|
||||
OpOr operatorOr = (OpOr) rootAst;
|
||||
@@ -289,7 +288,7 @@ public class SpelParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tokenKind() {
|
||||
void tokenKind() {
|
||||
TokenKind tk = TokenKind.NOT;
|
||||
assertThat(tk.hasPayload()).isFalse();
|
||||
assertThat(tk.toString()).isEqualTo("NOT(!)");
|
||||
@@ -304,7 +303,7 @@ public class SpelParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void token() {
|
||||
void token() {
|
||||
Token token = new Token(TokenKind.NOT, 0, 3);
|
||||
assertThat(token.kind).isEqualTo(TokenKind.NOT);
|
||||
assertThat(token.startPos).isEqualTo(0);
|
||||
@@ -319,7 +318,7 @@ public class SpelParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exceptions() {
|
||||
void exceptions() {
|
||||
ExpressionException exprEx = new ExpressionException("test");
|
||||
assertThat(exprEx.getSimpleMessage()).isEqualTo("test");
|
||||
assertThat(exprEx.toDetailedString()).isEqualTo("test");
|
||||
@@ -337,13 +336,13 @@ public class SpelParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseMethodsOnNumbers() {
|
||||
void parseMethodsOnNumbers() {
|
||||
checkNumber("3.14.toString()", "3.14", String.class);
|
||||
checkNumber("3.toString()", "3", String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void numerics() {
|
||||
void numerics() {
|
||||
checkNumber("2", 2, Integer.class);
|
||||
checkNumber("22", 22, Integer.class);
|
||||
checkNumber("+22", 22, Integer.class);
|
||||
@@ -385,8 +384,7 @@ public class SpelParserTests {
|
||||
|
||||
private void checkNumberError(String expression, SpelMessage expectedMessage) {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() ->
|
||||
parser.parseRaw(expression))
|
||||
assertThatExceptionOfType(SpelParseException.class).isThrownBy(() -> parser.parseRaw(expression))
|
||||
.satisfies(ex -> assertThat(ex.getMessageCode()).isEqualTo(expectedMessage));
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -30,7 +30,6 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Helper class that efficiently creates multiple {@link PreparedStatementCreator}
|
||||
@@ -200,9 +199,8 @@ public class PreparedStatementCreatorFactory {
|
||||
|
||||
public PreparedStatementCreatorImpl(String actualSql, List<?> parameters) {
|
||||
this.actualSql = actualSql;
|
||||
Assert.notNull(parameters, "Parameters List must not be null");
|
||||
this.parameters = parameters;
|
||||
if (this.parameters.size() != declaredParameters.size()) {
|
||||
if (parameters.size() != declaredParameters.size()) {
|
||||
// Account for named parameters being used multiple times
|
||||
Set<String> names = new HashSet<>();
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
|
||||
+2
-2
@@ -345,9 +345,9 @@ public abstract class NamedParameterUtils {
|
||||
for (int i = 0; i < paramNames.size(); i++) {
|
||||
String paramName = paramNames.get(i);
|
||||
try {
|
||||
Object value = paramSource.getValue(paramName);
|
||||
SqlParameter param = findParameter(declaredParams, paramName, i);
|
||||
paramArray[i] = (param != null ? new SqlParameterValue(param, value) : value);
|
||||
paramArray[i] = (param != null ? new SqlParameterValue(param, paramSource.getValue(paramName)) :
|
||||
SqlParameterSourceUtils.getTypedValue(paramSource, paramName));
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
|
||||
+2
-6
@@ -92,17 +92,13 @@ public abstract class SqlParameterSourceUtils {
|
||||
* @param source the source of parameter values and type information
|
||||
* @param parameterName the name of the parameter
|
||||
* @return the value object
|
||||
* @see SqlParameterValue
|
||||
*/
|
||||
@Nullable
|
||||
public static Object getTypedValue(SqlParameterSource source, String parameterName) {
|
||||
int sqlType = source.getSqlType(parameterName);
|
||||
if (sqlType != SqlParameterSource.TYPE_UNKNOWN) {
|
||||
if (source.getTypeName(parameterName) != null) {
|
||||
return new SqlParameterValue(sqlType, source.getTypeName(parameterName), source.getValue(parameterName));
|
||||
}
|
||||
else {
|
||||
return new SqlParameterValue(sqlType, source.getValue(parameterName));
|
||||
}
|
||||
return new SqlParameterValue(sqlType, source.getTypeName(parameterName), source.getValue(parameterName));
|
||||
}
|
||||
else {
|
||||
return source.getValue(parameterName);
|
||||
|
||||
+9
-6
@@ -561,10 +561,11 @@ public class NamedParameterJdbcTemplateTests {
|
||||
|
||||
@Test
|
||||
public void testBatchUpdateWithSqlParameterSourcePlusTypeInfo() throws Exception {
|
||||
SqlParameterSource[] ids = new SqlParameterSource[2];
|
||||
ids[0] = new MapSqlParameterSource().addValue("id", 100, Types.NUMERIC);
|
||||
ids[1] = new MapSqlParameterSource().addValue("id", 200, Types.NUMERIC);
|
||||
final int[] rowsAffected = new int[] {1, 2};
|
||||
SqlParameterSource[] ids = new SqlParameterSource[3];
|
||||
ids[0] = new MapSqlParameterSource().addValue("id", null, Types.NULL);
|
||||
ids[1] = new MapSqlParameterSource().addValue("id", 100, Types.NUMERIC);
|
||||
ids[2] = new MapSqlParameterSource().addValue("id", 200, Types.NUMERIC);
|
||||
final int[] rowsAffected = new int[] {1, 2, 3};
|
||||
|
||||
given(preparedStatement.executeBatch()).willReturn(rowsAffected);
|
||||
given(connection.getMetaData()).willReturn(databaseMetaData);
|
||||
@@ -572,13 +573,15 @@ public class NamedParameterJdbcTemplateTests {
|
||||
|
||||
int[] actualRowsAffected = namedParameterTemplate.batchUpdate(
|
||||
"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
|
||||
assertThat(actualRowsAffected.length == 2).as("executed 2 updates").isTrue();
|
||||
assertThat(actualRowsAffected.length == 3).as("executed 3 updates").isTrue();
|
||||
assertThat(actualRowsAffected[0]).isEqualTo(rowsAffected[0]);
|
||||
assertThat(actualRowsAffected[1]).isEqualTo(rowsAffected[1]);
|
||||
assertThat(actualRowsAffected[2]).isEqualTo(rowsAffected[2]);
|
||||
verify(connection).prepareStatement("UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?");
|
||||
verify(preparedStatement).setNull(1, Types.NULL);
|
||||
verify(preparedStatement).setObject(1, 100, Types.NUMERIC);
|
||||
verify(preparedStatement).setObject(1, 200, Types.NUMERIC);
|
||||
verify(preparedStatement, times(2)).addBatch();
|
||||
verify(preparedStatement, times(3)).addBatch();
|
||||
verify(preparedStatement, atLeastOnce()).close();
|
||||
verify(connection, atLeastOnce()).close();
|
||||
}
|
||||
|
||||
+1
@@ -94,6 +94,7 @@ public class KotlinSerializationJsonMessageConverter extends AbstractJsonMessage
|
||||
* Tries to find a serializer that can marshall or unmarshall instances of the given type
|
||||
* using kotlinx.serialization. If no serializer can be found, an exception is thrown.
|
||||
* <p>Resolved serializers are cached and cached results are returned on successive calls.
|
||||
* TODO Avoid relying on throwing exception when https://github.com/Kotlin/kotlinx.serialization/pull/1164 is fixed
|
||||
* @param type the type to find a serializer for
|
||||
* @return a resolved serializer for the given type
|
||||
* @throws RuntimeException if no serializer supporting the given type can be found
|
||||
|
||||
+10
-1
@@ -31,6 +31,8 @@ import io.rsocket.Payload;
|
||||
import io.rsocket.metadata.CompositeMetadata;
|
||||
import io.rsocket.metadata.RoutingMetadata;
|
||||
import io.rsocket.metadata.WellKnownMimeType;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.ResolvableType;
|
||||
@@ -52,6 +54,9 @@ import org.springframework.util.MimeType;
|
||||
*/
|
||||
public class DefaultMetadataExtractor implements MetadataExtractor, MetadataExtractorRegistry {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DefaultMetadataExtractor.class);
|
||||
|
||||
|
||||
private final List<Decoder<?>> decoders;
|
||||
|
||||
private final Map<String, EntryExtractor<?>> registrations = new HashMap<>();
|
||||
@@ -119,6 +124,10 @@ public class DefaultMetadataExtractor implements MetadataExtractor, MetadataExtr
|
||||
else {
|
||||
extractEntry(payload.metadata().slice(), metadataMimeType.toString(), result);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Values extracted from metadata: " + result +
|
||||
" with registrations for " + this.registrations.keySet() + ".");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -175,7 +184,7 @@ public class DefaultMetadataExtractor implements MetadataExtractor, MetadataExtr
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "mimeType=" + this.mimeType + ", targetType=" + this.targetType;
|
||||
return "\"" + this.mimeType + "\" => " + this.targetType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+85
-60
@@ -71,18 +71,18 @@ import org.springframework.validation.Validator;
|
||||
* Provides essential configuration for handling messages with simple messaging
|
||||
* protocols such as STOMP.
|
||||
*
|
||||
* <p>{@link #clientInboundChannel()} and {@link #clientOutboundChannel()} deliver
|
||||
* <p>{@link #clientInboundChannel(TaskExecutor)} and {@link #clientOutboundChannel(TaskExecutor)} deliver
|
||||
* messages to and from remote clients to several message handlers such as the
|
||||
* following.
|
||||
* <ul>
|
||||
* <li>{@link #simpAnnotationMethodMessageHandler()}</li>
|
||||
* <li>{@link #simpleBrokerMessageHandler()}</li>
|
||||
* <li>{@link #stompBrokerRelayMessageHandler()}</li>
|
||||
* <li>{@link #userDestinationMessageHandler()}</li>
|
||||
* <li>{@link #simpAnnotationMethodMessageHandler(AbstractSubscribableChannel, AbstractSubscribableChannel, SimpMessagingTemplate, CompositeMessageConverter)}</li>
|
||||
* <li>{@link #simpleBrokerMessageHandler(AbstractSubscribableChannel, AbstractSubscribableChannel, AbstractSubscribableChannel, UserDestinationResolver)}</li>
|
||||
* <li>{@link #stompBrokerRelayMessageHandler(AbstractSubscribableChannel, AbstractSubscribableChannel, AbstractSubscribableChannel, UserDestinationMessageHandler, MessageHandler, UserDestinationResolver)}</li>
|
||||
* <li>{@link #userDestinationMessageHandler(AbstractSubscribableChannel, AbstractSubscribableChannel, AbstractSubscribableChannel, UserDestinationResolver)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>{@link #brokerChannel()} delivers messages from within the application to the
|
||||
* the respective message handlers. {@link #brokerMessagingTemplate()} can be injected
|
||||
* <p>{@link #brokerChannel(AbstractSubscribableChannel, AbstractSubscribableChannel, TaskExecutor)} delivers messages from within the application to the
|
||||
* the respective message handlers. {@link #brokerMessagingTemplate(AbstractSubscribableChannel, AbstractSubscribableChannel, AbstractSubscribableChannel, CompositeMessageConverter)} can be injected
|
||||
* into any application component to send messages.
|
||||
*
|
||||
* <p>Subclasses are responsible for the parts of the configuration that feed messages
|
||||
@@ -90,6 +90,7 @@ import org.springframework.validation.Validator;
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
* @author Sebastien Deleuze
|
||||
* @since 4.0
|
||||
*/
|
||||
public abstract class AbstractMessageBrokerConfiguration implements ApplicationContextAware {
|
||||
@@ -147,8 +148,8 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
||||
|
||||
|
||||
@Bean
|
||||
public AbstractSubscribableChannel clientInboundChannel() {
|
||||
ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel(clientInboundChannelExecutor());
|
||||
public AbstractSubscribableChannel clientInboundChannel(TaskExecutor clientInboundChannelExecutor) {
|
||||
ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel(clientInboundChannelExecutor);
|
||||
channel.setLogger(SimpLogging.forLog(channel.getLogger()));
|
||||
ChannelRegistration reg = getClientInboundChannelRegistration();
|
||||
if (reg.hasInterceptors()) {
|
||||
@@ -183,8 +184,8 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AbstractSubscribableChannel clientOutboundChannel() {
|
||||
ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel(clientOutboundChannelExecutor());
|
||||
public AbstractSubscribableChannel clientOutboundChannel(TaskExecutor clientOutboundChannelExecutor) {
|
||||
ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel(clientOutboundChannelExecutor);
|
||||
channel.setLogger(SimpLogging.forLog(channel.getLogger()));
|
||||
ChannelRegistration reg = getClientOutboundChannelRegistration();
|
||||
if (reg.hasInterceptors()) {
|
||||
@@ -219,10 +220,11 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AbstractSubscribableChannel brokerChannel() {
|
||||
ChannelRegistration reg = getBrokerRegistry().getBrokerChannelRegistration();
|
||||
public AbstractSubscribableChannel brokerChannel(AbstractSubscribableChannel clientInboundChannel,
|
||||
AbstractSubscribableChannel clientOutboundChannel, TaskExecutor brokerChannelExecutor) {
|
||||
ChannelRegistration reg = getBrokerRegistry(clientInboundChannel, clientOutboundChannel).getBrokerChannelRegistration();
|
||||
ExecutorSubscribableChannel channel = (reg.hasTaskExecutor() ?
|
||||
new ExecutorSubscribableChannel(brokerChannelExecutor()) : new ExecutorSubscribableChannel());
|
||||
new ExecutorSubscribableChannel(brokerChannelExecutor) : new ExecutorSubscribableChannel());
|
||||
reg.interceptors(new ImmutableMessageChannelInterceptor());
|
||||
channel.setLogger(SimpLogging.forLog(channel.getLogger()));
|
||||
channel.setInterceptors(reg.getInterceptors());
|
||||
@@ -230,8 +232,9 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskExecutor brokerChannelExecutor() {
|
||||
ChannelRegistration reg = getBrokerRegistry().getBrokerChannelRegistration();
|
||||
public TaskExecutor brokerChannelExecutor(AbstractSubscribableChannel clientInboundChannel,
|
||||
AbstractSubscribableChannel clientOutboundChannel) {
|
||||
ChannelRegistration reg = getBrokerRegistry(clientInboundChannel, clientOutboundChannel).getBrokerChannelRegistration();
|
||||
ThreadPoolTaskExecutor executor;
|
||||
if (reg.hasTaskExecutor()) {
|
||||
executor = reg.taskExecutor().getTaskExecutor();
|
||||
@@ -251,9 +254,10 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
||||
* An accessor for the {@link MessageBrokerRegistry} that ensures its one-time creation
|
||||
* and initialization through {@link #configureMessageBroker(MessageBrokerRegistry)}.
|
||||
*/
|
||||
protected final MessageBrokerRegistry getBrokerRegistry() {
|
||||
protected final MessageBrokerRegistry getBrokerRegistry(AbstractSubscribableChannel clientInboundChannel,
|
||||
AbstractSubscribableChannel clientOutboundChannel) {
|
||||
if (this.brokerRegistry == null) {
|
||||
MessageBrokerRegistry registry = new MessageBrokerRegistry(clientInboundChannel(), clientOutboundChannel());
|
||||
MessageBrokerRegistry registry = new MessageBrokerRegistry(clientInboundChannel, clientOutboundChannel);
|
||||
configureMessageBroker(registry);
|
||||
this.brokerRegistry = registry;
|
||||
}
|
||||
@@ -272,15 +276,20 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
||||
* configuration classes.
|
||||
*/
|
||||
@Nullable
|
||||
public final PathMatcher getPathMatcher() {
|
||||
return getBrokerRegistry().getPathMatcher();
|
||||
public final PathMatcher getPathMatcher(AbstractSubscribableChannel clientInboundChannel,
|
||||
AbstractSubscribableChannel clientOutboundChannel) {
|
||||
return getBrokerRegistry(clientInboundChannel, clientOutboundChannel).getPathMatcher();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SimpAnnotationMethodMessageHandler simpAnnotationMethodMessageHandler() {
|
||||
SimpAnnotationMethodMessageHandler handler = createAnnotationMethodMessageHandler();
|
||||
handler.setDestinationPrefixes(getBrokerRegistry().getApplicationDestinationPrefixes());
|
||||
handler.setMessageConverter(brokerMessageConverter());
|
||||
public SimpAnnotationMethodMessageHandler simpAnnotationMethodMessageHandler(
|
||||
AbstractSubscribableChannel clientInboundChannel, AbstractSubscribableChannel clientOutboundChannel,
|
||||
SimpMessagingTemplate brokerMessagingTemplate, CompositeMessageConverter brokerMessageConverter) {
|
||||
SimpAnnotationMethodMessageHandler handler = createAnnotationMethodMessageHandler(clientInboundChannel,
|
||||
clientOutboundChannel, brokerMessagingTemplate);
|
||||
MessageBrokerRegistry brokerRegistry = getBrokerRegistry(clientInboundChannel, clientOutboundChannel);
|
||||
handler.setDestinationPrefixes(brokerRegistry.getApplicationDestinationPrefixes());
|
||||
handler.setMessageConverter(brokerMessageConverter);
|
||||
handler.setValidator(simpValidator());
|
||||
|
||||
List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
|
||||
@@ -291,7 +300,7 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
||||
addReturnValueHandlers(returnValueHandlers);
|
||||
handler.setCustomReturnValueHandlers(returnValueHandlers);
|
||||
|
||||
PathMatcher pathMatcher = getBrokerRegistry().getPathMatcher();
|
||||
PathMatcher pathMatcher = brokerRegistry.getPathMatcher();
|
||||
if (pathMatcher != null) {
|
||||
handler.setPathMatcher(pathMatcher);
|
||||
}
|
||||
@@ -302,11 +311,12 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
||||
* Protected method for plugging in a custom subclass of
|
||||
* {@link org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler
|
||||
* SimpAnnotationMethodMessageHandler}.
|
||||
* @since 4.2
|
||||
* @since 5.3.2
|
||||
*/
|
||||
protected SimpAnnotationMethodMessageHandler createAnnotationMethodMessageHandler() {
|
||||
return new SimpAnnotationMethodMessageHandler(clientInboundChannel(),
|
||||
clientOutboundChannel(), brokerMessagingTemplate());
|
||||
protected SimpAnnotationMethodMessageHandler createAnnotationMethodMessageHandler(
|
||||
AbstractSubscribableChannel clientInboundChannel, AbstractSubscribableChannel clientOutboundChannel,
|
||||
SimpMessagingTemplate brokerMessagingTemplate) {
|
||||
return new SimpAnnotationMethodMessageHandler(clientInboundChannel, clientOutboundChannel, brokerMessagingTemplate);
|
||||
}
|
||||
|
||||
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
@@ -317,48 +327,56 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
||||
|
||||
@Bean
|
||||
@Nullable
|
||||
public AbstractBrokerMessageHandler simpleBrokerMessageHandler() {
|
||||
SimpleBrokerMessageHandler handler = getBrokerRegistry().getSimpleBroker(brokerChannel());
|
||||
public AbstractBrokerMessageHandler simpleBrokerMessageHandler(AbstractSubscribableChannel clientInboundChannel,
|
||||
AbstractSubscribableChannel clientOutboundChannel, AbstractSubscribableChannel brokerChannel,
|
||||
UserDestinationResolver userDestinationResolver) {
|
||||
SimpleBrokerMessageHandler handler = getBrokerRegistry(clientInboundChannel, clientOutboundChannel).getSimpleBroker(brokerChannel);
|
||||
if (handler == null) {
|
||||
return null;
|
||||
}
|
||||
updateUserDestinationResolver(handler);
|
||||
updateUserDestinationResolver(handler, userDestinationResolver);
|
||||
return handler;
|
||||
}
|
||||
|
||||
private void updateUserDestinationResolver(AbstractBrokerMessageHandler handler) {
|
||||
private void updateUserDestinationResolver(AbstractBrokerMessageHandler handler, UserDestinationResolver userDestinationResolver) {
|
||||
Collection<String> prefixes = handler.getDestinationPrefixes();
|
||||
if (!prefixes.isEmpty() && !prefixes.iterator().next().startsWith("/")) {
|
||||
((DefaultUserDestinationResolver) userDestinationResolver()).setRemoveLeadingSlash(true);
|
||||
((DefaultUserDestinationResolver) userDestinationResolver).setRemoveLeadingSlash(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Nullable
|
||||
public AbstractBrokerMessageHandler stompBrokerRelayMessageHandler() {
|
||||
StompBrokerRelayMessageHandler handler = getBrokerRegistry().getStompBrokerRelay(brokerChannel());
|
||||
public AbstractBrokerMessageHandler stompBrokerRelayMessageHandler(AbstractSubscribableChannel clientInboundChannel,
|
||||
AbstractSubscribableChannel clientOutboundChannel, AbstractSubscribableChannel brokerChannel,
|
||||
UserDestinationMessageHandler userDestinationMessageHandler, @Nullable MessageHandler userRegistryMessageHandler,
|
||||
UserDestinationResolver userDestinationResolver) {
|
||||
MessageBrokerRegistry brokerRegistry = getBrokerRegistry(clientInboundChannel, clientOutboundChannel);
|
||||
StompBrokerRelayMessageHandler handler = brokerRegistry.getStompBrokerRelay(brokerChannel);
|
||||
if (handler == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, MessageHandler> subscriptions = new HashMap<>(4);
|
||||
String destination = getBrokerRegistry().getUserDestinationBroadcast();
|
||||
String destination = brokerRegistry.getUserDestinationBroadcast();
|
||||
if (destination != null) {
|
||||
subscriptions.put(destination, userDestinationMessageHandler());
|
||||
subscriptions.put(destination, userDestinationMessageHandler);
|
||||
}
|
||||
destination = getBrokerRegistry().getUserRegistryBroadcast();
|
||||
destination = brokerRegistry.getUserRegistryBroadcast();
|
||||
if (destination != null) {
|
||||
subscriptions.put(destination, userRegistryMessageHandler());
|
||||
subscriptions.put(destination, userRegistryMessageHandler);
|
||||
}
|
||||
handler.setSystemSubscriptions(subscriptions);
|
||||
updateUserDestinationResolver(handler);
|
||||
updateUserDestinationResolver(handler, userDestinationResolver);
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UserDestinationMessageHandler userDestinationMessageHandler() {
|
||||
UserDestinationMessageHandler handler = new UserDestinationMessageHandler(clientInboundChannel(),
|
||||
brokerChannel(), userDestinationResolver());
|
||||
String destination = getBrokerRegistry().getUserDestinationBroadcast();
|
||||
public UserDestinationMessageHandler userDestinationMessageHandler(AbstractSubscribableChannel clientInboundChannel,
|
||||
AbstractSubscribableChannel clientOutboundChannel, AbstractSubscribableChannel brokerChannel,
|
||||
UserDestinationResolver userDestinationResolver) {
|
||||
UserDestinationMessageHandler handler = new UserDestinationMessageHandler(clientInboundChannel,
|
||||
brokerChannel, userDestinationResolver);
|
||||
String destination = getBrokerRegistry(clientInboundChannel, clientOutboundChannel).getUserDestinationBroadcast();
|
||||
if (destination != null) {
|
||||
handler.setBroadcastDestination(destination);
|
||||
}
|
||||
@@ -367,15 +385,17 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
||||
|
||||
@Bean
|
||||
@Nullable
|
||||
public MessageHandler userRegistryMessageHandler() {
|
||||
if (getBrokerRegistry().getUserRegistryBroadcast() == null) {
|
||||
public MessageHandler userRegistryMessageHandler(AbstractSubscribableChannel clientInboundChannel,
|
||||
AbstractSubscribableChannel clientOutboundChannel, SimpUserRegistry userRegistry,
|
||||
SimpMessagingTemplate brokerMessagingTemplate, TaskScheduler messageBrokerTaskScheduler) {
|
||||
MessageBrokerRegistry brokerRegistry = getBrokerRegistry(clientInboundChannel, clientOutboundChannel);
|
||||
if (brokerRegistry.getUserRegistryBroadcast() == null) {
|
||||
return null;
|
||||
}
|
||||
SimpUserRegistry userRegistry = userRegistry();
|
||||
Assert.isInstanceOf(MultiServerUserRegistry.class, userRegistry, "MultiServerUserRegistry required");
|
||||
return new UserRegistryMessageHandler((MultiServerUserRegistry) userRegistry,
|
||||
brokerMessagingTemplate(), getBrokerRegistry().getUserRegistryBroadcast(),
|
||||
messageBrokerTaskScheduler());
|
||||
brokerMessagingTemplate, brokerRegistry.getUserRegistryBroadcast(),
|
||||
messageBrokerTaskScheduler);
|
||||
}
|
||||
|
||||
// Expose alias for 4.1 compatibility
|
||||
@@ -389,13 +409,15 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SimpMessagingTemplate brokerMessagingTemplate() {
|
||||
SimpMessagingTemplate template = new SimpMessagingTemplate(brokerChannel());
|
||||
String prefix = getBrokerRegistry().getUserDestinationPrefix();
|
||||
public SimpMessagingTemplate brokerMessagingTemplate(AbstractSubscribableChannel brokerChannel,
|
||||
AbstractSubscribableChannel clientInboundChannel, AbstractSubscribableChannel clientOutboundChannel,
|
||||
CompositeMessageConverter brokerMessageConverter) {
|
||||
SimpMessagingTemplate template = new SimpMessagingTemplate(brokerChannel);
|
||||
String prefix = getBrokerRegistry(clientInboundChannel, clientOutboundChannel).getUserDestinationPrefix();
|
||||
if (prefix != null) {
|
||||
template.setUserDestinationPrefix(prefix);
|
||||
}
|
||||
template.setMessageConverter(brokerMessageConverter());
|
||||
template.setMessageConverter(brokerMessageConverter);
|
||||
return template;
|
||||
}
|
||||
|
||||
@@ -441,9 +463,10 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UserDestinationResolver userDestinationResolver() {
|
||||
DefaultUserDestinationResolver resolver = new DefaultUserDestinationResolver(userRegistry());
|
||||
String prefix = getBrokerRegistry().getUserDestinationPrefix();
|
||||
public UserDestinationResolver userDestinationResolver(SimpUserRegistry userRegistry,
|
||||
AbstractSubscribableChannel clientInboundChannel, AbstractSubscribableChannel clientOutboundChannel) {
|
||||
DefaultUserDestinationResolver resolver = new DefaultUserDestinationResolver(userRegistry);
|
||||
String prefix = getBrokerRegistry(clientInboundChannel, clientOutboundChannel).getUserDestinationPrefix();
|
||||
if (prefix != null) {
|
||||
resolver.setUserDestinationPrefix(prefix);
|
||||
}
|
||||
@@ -452,12 +475,14 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("deprecation")
|
||||
public SimpUserRegistry userRegistry() {
|
||||
public SimpUserRegistry userRegistry(AbstractSubscribableChannel clientInboundChannel,
|
||||
AbstractSubscribableChannel clientOutboundChannel) {
|
||||
SimpUserRegistry registry = createLocalUserRegistry();
|
||||
MessageBrokerRegistry brokerRegistry = getBrokerRegistry(clientInboundChannel, clientOutboundChannel);
|
||||
if (registry == null) {
|
||||
registry = createLocalUserRegistry(getBrokerRegistry().getUserRegistryOrder());
|
||||
registry = createLocalUserRegistry(brokerRegistry.getUserRegistryOrder());
|
||||
}
|
||||
boolean broadcast = getBrokerRegistry().getUserRegistryBroadcast() != null;
|
||||
boolean broadcast = brokerRegistry.getUserRegistryBroadcast() != null;
|
||||
return (broadcast ? new MultiServerUserRegistry(registry) : registry);
|
||||
}
|
||||
|
||||
|
||||
+14
-12
@@ -378,13 +378,14 @@ public class MessageHeaderAccessor {
|
||||
* {@link #copyHeadersIfAbsent(Map)} to avoid overwriting values.
|
||||
*/
|
||||
public void copyHeaders(@Nullable Map<String, ?> headersToCopy) {
|
||||
if (headersToCopy != null) {
|
||||
headersToCopy.forEach((key, value) -> {
|
||||
if (!isReadOnly(key)) {
|
||||
setHeader(key, value);
|
||||
}
|
||||
});
|
||||
if (headersToCopy == null || this.headers == headersToCopy) {
|
||||
return;
|
||||
}
|
||||
headersToCopy.forEach((key, value) -> {
|
||||
if (!isReadOnly(key)) {
|
||||
setHeader(key, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -392,13 +393,14 @@ public class MessageHeaderAccessor {
|
||||
* <p>This operation will <em>not</em> overwrite any existing values.
|
||||
*/
|
||||
public void copyHeadersIfAbsent(@Nullable Map<String, ?> headersToCopy) {
|
||||
if (headersToCopy != null) {
|
||||
headersToCopy.forEach((key, value) -> {
|
||||
if (!isReadOnly(key)) {
|
||||
setHeaderIfAbsent(key, value);
|
||||
}
|
||||
});
|
||||
if (headersToCopy == null || this.headers == headersToCopy) {
|
||||
return;
|
||||
}
|
||||
headersToCopy.forEach((key, value) -> {
|
||||
if (!isReadOnly(key)) {
|
||||
setHeaderIfAbsent(key, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected boolean isReadOnly(String headerName) {
|
||||
|
||||
+51
-20
@@ -75,6 +75,8 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, List<String>> map = (Map<String, List<String>>) getHeader(NATIVE_HEADERS);
|
||||
if (map != null) {
|
||||
// setHeader checks for equality but we need copy of native headers
|
||||
setHeader(NATIVE_HEADERS, null);
|
||||
setHeader(NATIVE_HEADERS, new LinkedMultiValueMap<>(map));
|
||||
}
|
||||
}
|
||||
@@ -103,6 +105,8 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
|
||||
if (isMutable()) {
|
||||
Map<String, List<String>> map = getNativeHeaders();
|
||||
if (map != null) {
|
||||
// setHeader checks for equality but we need immutable wrapper
|
||||
setHeader(NATIVE_HEADERS, null);
|
||||
setHeader(NATIVE_HEADERS, Collections.unmodifiableMap(map));
|
||||
}
|
||||
super.setImmutable();
|
||||
@@ -110,31 +114,34 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeader(String name, @Nullable Object value) {
|
||||
if (name.equalsIgnoreCase(NATIVE_HEADERS)) {
|
||||
// Force removal since setHeader checks for equality
|
||||
super.setHeader(NATIVE_HEADERS, null);
|
||||
public void copyHeaders(@Nullable Map<String, ?> headersToCopy) {
|
||||
if (headersToCopy == null) {
|
||||
return;
|
||||
}
|
||||
super.setHeader(name, value);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, List<String>> map = (Map<String, List<String>>) headersToCopy.get(NATIVE_HEADERS);
|
||||
if (map != null && map != getNativeHeaders()) {
|
||||
map.forEach(this::setNativeHeaderValues);
|
||||
}
|
||||
|
||||
// setHeader checks for equality, native headers should be equal by now
|
||||
super.copyHeaders(headersToCopy);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void copyHeaders(@Nullable Map<String, ?> headersToCopy) {
|
||||
if (headersToCopy != null) {
|
||||
Map<String, List<String>> nativeHeaders = getNativeHeaders();
|
||||
Map<String, List<String>> map = (Map<String, List<String>>) headersToCopy.get(NATIVE_HEADERS);
|
||||
if (map != null) {
|
||||
if (nativeHeaders != null) {
|
||||
nativeHeaders.putAll(map);
|
||||
}
|
||||
else {
|
||||
nativeHeaders = new LinkedMultiValueMap<>(map);
|
||||
}
|
||||
}
|
||||
super.copyHeaders(headersToCopy);
|
||||
setHeader(NATIVE_HEADERS, nativeHeaders);
|
||||
public void copyHeadersIfAbsent(@Nullable Map<String, ?> headersToCopy) {
|
||||
if (headersToCopy == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, List<String>> map = (Map<String, List<String>>) headersToCopy.get(NATIVE_HEADERS);
|
||||
if (map != null && getNativeHeaders() == null) {
|
||||
map.forEach(this::setNativeHeaderValues);
|
||||
}
|
||||
|
||||
super.copyHeadersIfAbsent(headersToCopy);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,6 +208,30 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant of {@link #addNativeHeader(String, String)} for all values.
|
||||
* @since 5.2.12
|
||||
*/
|
||||
public void setNativeHeaderValues(String name, @Nullable List<String> values) {
|
||||
Assert.state(isMutable(), "Already immutable");
|
||||
Map<String, List<String>> map = getNativeHeaders();
|
||||
if (values == null) {
|
||||
if (map != null && map.get(name) != null) {
|
||||
setModified(true);
|
||||
map.remove(name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (map == null) {
|
||||
map = new LinkedMultiValueMap<>(3);
|
||||
setHeader(NATIVE_HEADERS, map);
|
||||
}
|
||||
if (!ObjectUtils.nullSafeEquals(values, getHeader(name))) {
|
||||
setModified(true);
|
||||
map.put(name, new ArrayList<>(values));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the specified native header value to existing values.
|
||||
* <p>In order for this to work, the accessor must be {@link #isMutable()
|
||||
|
||||
+10
-7
@@ -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.
|
||||
@@ -30,6 +30,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
@@ -594,19 +595,20 @@ public class MessageBrokerConfigurationTests {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public AbstractSubscribableChannel clientInboundChannel() {
|
||||
public AbstractSubscribableChannel clientInboundChannel(TaskExecutor clientInboundChannelExecutor) {
|
||||
return new TestChannel();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public AbstractSubscribableChannel clientOutboundChannel() {
|
||||
public AbstractSubscribableChannel clientOutboundChannel(TaskExecutor clientOutboundChannelExecutor) {
|
||||
return new TestChannel();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public AbstractSubscribableChannel brokerChannel() {
|
||||
public AbstractSubscribableChannel brokerChannel(AbstractSubscribableChannel clientInboundChannel,
|
||||
AbstractSubscribableChannel clientOutboundChannel, TaskExecutor brokerChannelExecutor) {
|
||||
return new TestChannel();
|
||||
}
|
||||
}
|
||||
@@ -680,20 +682,21 @@ public class MessageBrokerConfigurationTests {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public AbstractSubscribableChannel clientInboundChannel() {
|
||||
public AbstractSubscribableChannel clientInboundChannel(TaskExecutor clientInboundChannelExecutor) {
|
||||
// synchronous
|
||||
return new ExecutorSubscribableChannel(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public AbstractSubscribableChannel clientOutboundChannel() {
|
||||
public AbstractSubscribableChannel clientOutboundChannel(TaskExecutor clientOutboundChannelExecutor) {
|
||||
return new TestChannel();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public AbstractSubscribableChannel brokerChannel() {
|
||||
public AbstractSubscribableChannel brokerChannel(AbstractSubscribableChannel clientInboundChannel,
|
||||
AbstractSubscribableChannel clientOutboundChannel, TaskExecutor brokerChannelExecutor) {
|
||||
// synchronous
|
||||
return new ExecutorSubscribableChannel(null);
|
||||
}
|
||||
|
||||
+35
-8
@@ -226,19 +226,46 @@ public class NativeMessageHeaderAccessorTests {
|
||||
|
||||
@Test // gh-25821
|
||||
void copyImmutableToMutable() {
|
||||
NativeMessageHeaderAccessor source = new NativeMessageHeaderAccessor();
|
||||
source.addNativeHeader("foo", "bar");
|
||||
Message<String> message = MessageBuilder.createMessage("payload", source.getMessageHeaders());
|
||||
NativeMessageHeaderAccessor sourceAccessor = new NativeMessageHeaderAccessor();
|
||||
sourceAccessor.addNativeHeader("foo", "bar");
|
||||
Message<String> source = MessageBuilder.createMessage("payload", sourceAccessor.getMessageHeaders());
|
||||
|
||||
NativeMessageHeaderAccessor target = new NativeMessageHeaderAccessor();
|
||||
target.copyHeaders(message.getHeaders());
|
||||
target.setLeaveMutable(true);
|
||||
message = MessageBuilder.createMessage(message.getPayload(), target.getMessageHeaders());
|
||||
NativeMessageHeaderAccessor targetAccessor = new NativeMessageHeaderAccessor();
|
||||
targetAccessor.copyHeaders(source.getHeaders());
|
||||
targetAccessor.setLeaveMutable(true);
|
||||
Message<?> target = MessageBuilder.createMessage(source.getPayload(), targetAccessor.getMessageHeaders());
|
||||
|
||||
MessageHeaderAccessor accessor = MessageHeaderAccessor.getMutableAccessor(message);
|
||||
MessageHeaderAccessor accessor = MessageHeaderAccessor.getMutableAccessor(target);
|
||||
assertThat(accessor.isMutable());
|
||||
((NativeMessageHeaderAccessor) accessor).addNativeHeader("foo", "baz");
|
||||
assertThat(((NativeMessageHeaderAccessor) accessor).getNativeHeader("foo")).containsExactly("bar", "baz");
|
||||
}
|
||||
|
||||
@Test // gh-25821
|
||||
void copyIfAbsentImmutableToMutable() {
|
||||
NativeMessageHeaderAccessor sourceAccessor = new NativeMessageHeaderAccessor();
|
||||
sourceAccessor.addNativeHeader("foo", "bar");
|
||||
Message<String> source = MessageBuilder.createMessage("payload", sourceAccessor.getMessageHeaders());
|
||||
|
||||
MessageHeaderAccessor targetAccessor = new NativeMessageHeaderAccessor();
|
||||
targetAccessor.copyHeadersIfAbsent(source.getHeaders());
|
||||
targetAccessor.setLeaveMutable(true);
|
||||
Message<?> target = MessageBuilder.createMessage(source.getPayload(), targetAccessor.getMessageHeaders());
|
||||
|
||||
MessageHeaderAccessor accessor = MessageHeaderAccessor.getMutableAccessor(target);
|
||||
assertThat(accessor.isMutable());
|
||||
((NativeMessageHeaderAccessor) accessor).addNativeHeader("foo", "baz");
|
||||
assertThat(((NativeMessageHeaderAccessor) accessor).getNativeHeader("foo")).containsExactly("bar", "baz");
|
||||
}
|
||||
|
||||
@Test // gh-26155
|
||||
void copySelf() {
|
||||
NativeMessageHeaderAccessor accessor = new NativeMessageHeaderAccessor();
|
||||
accessor.addNativeHeader("foo", "bar");
|
||||
accessor.setHeader("otherHeader", "otherHeaderValue");
|
||||
accessor.setLeaveMutable(true);
|
||||
|
||||
// Does not fail with ConcurrentModificationException
|
||||
accessor.copyHeaders(accessor.getMessageHeaders());
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -418,10 +418,13 @@ public abstract class AbstractEntityManagerFactoryBean implements
|
||||
String message = ex.getMessage();
|
||||
String causeString = cause.toString();
|
||||
if (!message.endsWith(causeString)) {
|
||||
throw new PersistenceException(message + "; nested exception is " + causeString, cause);
|
||||
ex = new PersistenceException(message + "; nested exception is " + causeString, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Failed to initialize JPA EntityManagerFactory: " + ex.getMessage());
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -48,9 +48,9 @@ import org.springframework.lang.Nullable;
|
||||
* EntityManager. Developed and tested against Hibernate 5.3 and 5.4;
|
||||
* backwards-compatible with Hibernate 5.2 at runtime on a best-effort basis.
|
||||
*
|
||||
* <p>Exposes Hibernate's persistence provider and EntityManager extension interface,
|
||||
* and adapts {@link AbstractJpaVendorAdapter}'s common configuration settings.
|
||||
* Also supports the detection of annotated packages (through
|
||||
* <p>Exposes Hibernate's persistence provider and Hibernate's Session as extended
|
||||
* EntityManager interface, and adapts {@link AbstractJpaVendorAdapter}'s common
|
||||
* configuration settings. Also supports the detection of annotated packages (through
|
||||
* {@link org.springframework.orm.jpa.persistenceunit.SmartPersistenceUnitInfo#getManagedPackages()}),
|
||||
* e.g. containing Hibernate {@link org.hibernate.annotations.FilterDef} annotations,
|
||||
* along with Spring-driven entity scanning which requires no {@code persistence.xml}
|
||||
@@ -82,8 +82,8 @@ public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter {
|
||||
|
||||
public HibernateJpaVendorAdapter() {
|
||||
this.persistenceProvider = new SpringHibernateJpaPersistenceProvider();
|
||||
this.entityManagerFactoryInterface = SessionFactory.class;
|
||||
this.entityManagerInterface = Session.class;
|
||||
this.entityManagerFactoryInterface = SessionFactory.class; // as of Spring 5.3
|
||||
this.entityManagerInterface = Session.class; // as of Spring 5.3
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -41,6 +41,11 @@ import java.lang.annotation.Target;
|
||||
* is resolved. Typically, method references are used to supply values, as in the
|
||||
* example below.
|
||||
*
|
||||
* <p>As of Spring Framework 5.3.2, dynamic properties from methods annotated with
|
||||
* {@code @DynamicPropertySource} will be <em>inherited</em> from enclosing test
|
||||
* classes, analogous to inheritance from superclasses and interfaces. See
|
||||
* {@link NestedTestConfiguration @NestedTestConfiguration} for details.
|
||||
*
|
||||
* <p><strong>NOTE</strong>: if you use {@code @DynamicPropertySource} in a base
|
||||
* class and discover that tests in subclasses fail because the dynamic properties
|
||||
* change between subclasses, you may need to annotate your base class with
|
||||
|
||||
+1
@@ -74,6 +74,7 @@ import org.springframework.lang.Nullable;
|
||||
* <li>{@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}</li>
|
||||
* <li>{@link ActiveProfiles @ActiveProfiles}</li>
|
||||
* <li>{@link TestPropertySource @TestPropertySource}</li>
|
||||
* <li>{@link DynamicPropertySource @DynamicPropertySource}</li>
|
||||
* <li>{@link org.springframework.test.annotation.DirtiesContext @DirtiesContext}</li>
|
||||
* <li>{@link org.springframework.transaction.annotation.Transactional @Transactional}</li>
|
||||
* <li>{@link org.springframework.test.annotation.Rollback @Rollback}</li>
|
||||
|
||||
+82
-4
@@ -16,11 +16,19 @@
|
||||
|
||||
package org.springframework.test.context.junit.jupiter;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.AfterAllCallback;
|
||||
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
|
||||
@@ -33,15 +41,22 @@ import org.junit.jupiter.api.extension.ExtensionContext.Store;
|
||||
import org.junit.jupiter.api.extension.ParameterContext;
|
||||
import org.junit.jupiter.api.extension.ParameterResolver;
|
||||
import org.junit.jupiter.api.extension.TestInstancePostProcessor;
|
||||
import org.junit.platform.commons.annotation.Testable;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.ParameterResolutionDelegate;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
|
||||
import org.springframework.core.annotation.RepeatableContainers;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.TestConstructor;
|
||||
import org.springframework.test.context.TestContextManager;
|
||||
import org.springframework.test.context.support.PropertyProvider;
|
||||
import org.springframework.test.context.support.TestConstructorUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
|
||||
/**
|
||||
* {@code SpringExtension} integrates the <em>Spring TestContext Framework</em>
|
||||
@@ -64,10 +79,29 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes
|
||||
ParameterResolver {
|
||||
|
||||
/**
|
||||
* {@link Namespace} in which {@code TestContextManagers} are stored,
|
||||
* keyed by test class.
|
||||
* {@link Namespace} in which {@code TestContextManagers} are stored, keyed
|
||||
* by test class.
|
||||
*/
|
||||
private static final Namespace NAMESPACE = Namespace.create(SpringExtension.class);
|
||||
private static final Namespace TEST_CONTEXT_MANAGER_NAMESPACE = Namespace.create(SpringExtension.class);
|
||||
|
||||
/**
|
||||
* {@link Namespace} in which {@code @Autowired} validation error messages
|
||||
* are stored, keyed by test class.
|
||||
*/
|
||||
private static final Namespace AUTOWIRED_VALIDATION_NAMESPACE = Namespace.create(SpringExtension.class.getName() +
|
||||
"#autowired.validation");
|
||||
|
||||
private static final String NO_AUTOWIRED_VIOLATIONS_DETECTED = "NO AUTOWIRED VIOLATIONS DETECTED";
|
||||
|
||||
// Note that @Test, @TestFactory, @TestTemplate, @RepeatedTest, and @ParameterizedTest
|
||||
// are all meta-annotated with @Testable.
|
||||
private static final List<Class<? extends Annotation>> JUPITER_ANNOTATION_TYPES =
|
||||
Arrays.asList(BeforeAll.class, AfterAll.class, BeforeEach.class, AfterEach.class, Testable.class);
|
||||
|
||||
private static final MethodFilter autowiredTestOrLifecycleMethodFilter =
|
||||
ReflectionUtils.USER_DECLARED_METHODS
|
||||
.and(method -> !Modifier.isPrivate(method.getModifiers()))
|
||||
.and(SpringExtension::isAutowiredTestOrLifecycleMethod);
|
||||
|
||||
|
||||
/**
|
||||
@@ -93,12 +127,42 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes
|
||||
|
||||
/**
|
||||
* Delegates to {@link TestContextManager#prepareTestInstance}.
|
||||
* <p>As of Spring Framework 5.3.2, this method also validates that test
|
||||
* methods and test lifecycle methods are not annotated with
|
||||
* {@link Autowired @Autowired}.
|
||||
*/
|
||||
@Override
|
||||
public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
|
||||
validateAutowiredConfig(context);
|
||||
getTestContextManager(context).prepareTestInstance(testInstance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that test methods and test lifecycle methods in the supplied
|
||||
* test class are not annotated with {@link Autowired @Autowired}.
|
||||
* @since 5.3.2
|
||||
*/
|
||||
private void validateAutowiredConfig(ExtensionContext context) {
|
||||
// We save the result in the ExtensionContext.Store so that we don't
|
||||
// re-validate all methods for the same test class multiple times.
|
||||
Store store = context.getStore(AUTOWIRED_VALIDATION_NAMESPACE);
|
||||
String errorMessage = store.getOrComputeIfAbsent(context.getRequiredTestClass(),
|
||||
testClass -> {
|
||||
Method[] methodsWithErrors =
|
||||
ReflectionUtils.getUniqueDeclaredMethods(testClass, autowiredTestOrLifecycleMethodFilter);
|
||||
return (methodsWithErrors.length == 0 ? NO_AUTOWIRED_VIOLATIONS_DETECTED :
|
||||
String.format(
|
||||
"Test methods and test lifecycle methods must not be annotated with @Autowired. " +
|
||||
"You should instead annotate individual method parameters with @Autowired, " +
|
||||
"@Qualifier, or @Value. Offending methods in test class %s: %s",
|
||||
testClass.getName(), Arrays.toString(methodsWithErrors)));
|
||||
}, String.class);
|
||||
|
||||
if (errorMessage != NO_AUTOWIRED_VIOLATIONS_DETECTED) {
|
||||
throw new IllegalStateException(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to {@link TestContextManager#beforeTestMethod}.
|
||||
*/
|
||||
@@ -219,7 +283,21 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes
|
||||
}
|
||||
|
||||
private static Store getStore(ExtensionContext context) {
|
||||
return context.getRoot().getStore(NAMESPACE);
|
||||
return context.getRoot().getStore(TEST_CONTEXT_MANAGER_NAMESPACE);
|
||||
}
|
||||
|
||||
private static boolean isAutowiredTestOrLifecycleMethod(Method method) {
|
||||
MergedAnnotations mergedAnnotations =
|
||||
MergedAnnotations.from(method, SearchStrategy.DIRECT, RepeatableContainers.none());
|
||||
if (!mergedAnnotations.isPresent(Autowired.class)) {
|
||||
return false;
|
||||
}
|
||||
for (Class<? extends Annotation> annotationType : JUPITER_ANNOTATION_TYPES) {
|
||||
if (mergedAnnotations.isPresent(annotationType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-1
@@ -17,6 +17,7 @@
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -26,12 +27,14 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
import org.springframework.test.context.DynamicPropertySource;
|
||||
import org.springframework.test.context.TestContextAnnotationUtils;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizerFactory} to support
|
||||
* {@link DynamicPropertySource @DynamicPropertySource} methods.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 5.2.5
|
||||
* @see DynamicPropertiesContextCustomizer
|
||||
*/
|
||||
@@ -42,13 +45,21 @@ class DynamicPropertiesContextCustomizerFactory implements ContextCustomizerFact
|
||||
public DynamicPropertiesContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
|
||||
Set<Method> methods = MethodIntrospector.selectMethods(testClass, this::isAnnotated);
|
||||
Set<Method> methods = new LinkedHashSet<>();
|
||||
findMethods(testClass, methods);
|
||||
if (methods.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new DynamicPropertiesContextCustomizer(methods);
|
||||
}
|
||||
|
||||
private void findMethods(Class<?> testClass, Set<Method> methods) {
|
||||
methods.addAll(MethodIntrospector.selectMethods(testClass, this::isAnnotated));
|
||||
if (TestContextAnnotationUtils.searchEnclosingClass(testClass)) {
|
||||
findMethods(testClass.getEnclosingClass(), methods);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAnnotated(Method method) {
|
||||
return MergedAnnotations.from(method).isPresent(DynamicPropertySource.class);
|
||||
}
|
||||
|
||||
+4
-4
@@ -22,7 +22,6 @@ import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -46,6 +45,7 @@ import org.springframework.transaction.interceptor.TransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.TransactionAttributeSource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -462,9 +462,9 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
|
||||
* as well as annotated interface default methods
|
||||
*/
|
||||
private List<Method> getAnnotatedMethods(Class<?> clazz, Class<? extends Annotation> annotationType) {
|
||||
return Arrays.stream(ReflectionUtils.getUniqueDeclaredMethods(clazz, ReflectionUtils.USER_DECLARED_METHODS))
|
||||
.filter(method -> AnnotatedElementUtils.hasAnnotation(method, annotationType))
|
||||
.collect(Collectors.toList());
|
||||
MethodFilter methodFilter = ReflectionUtils.USER_DECLARED_METHODS
|
||||
.and(method -> AnnotatedElementUtils.hasAnnotation(method, annotationType));
|
||||
return Arrays.asList(ReflectionUtils.getUniqueDeclaredMethods(clazz, methodFilter));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+22
-2
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.test.web.servlet.request;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -24,12 +25,14 @@ import java.util.List;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.Part;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.mock.web.MockMultipartHttpServletRequest;
|
||||
import org.springframework.mock.web.MockPart;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
@@ -119,7 +122,7 @@ public class MockMultipartHttpServletRequestBuilder extends MockHttpServletReque
|
||||
if (parent instanceof MockMultipartHttpServletRequestBuilder) {
|
||||
MockMultipartHttpServletRequestBuilder parentBuilder = (MockMultipartHttpServletRequestBuilder) parent;
|
||||
this.files.addAll(parentBuilder.files);
|
||||
parentBuilder.parts.keySet().stream().forEach(name ->
|
||||
parentBuilder.parts.keySet().forEach(name ->
|
||||
this.parts.putIfAbsent(name, parentBuilder.parts.get(name)));
|
||||
}
|
||||
|
||||
@@ -138,9 +141,26 @@ public class MockMultipartHttpServletRequestBuilder extends MockHttpServletReque
|
||||
@Override
|
||||
protected final MockHttpServletRequest createServletRequest(ServletContext servletContext) {
|
||||
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(servletContext);
|
||||
this.files.forEach(request::addFile);
|
||||
this.files.forEach(file -> request.addPart(toMockPart(file)));
|
||||
this.parts.values().stream().flatMap(Collection::stream).forEach(request::addPart);
|
||||
return request;
|
||||
}
|
||||
|
||||
private MockPart toMockPart(MockMultipartFile file) {
|
||||
byte[] bytes = null;
|
||||
if (!file.isEmpty()) {
|
||||
try {
|
||||
bytes = file.getBytes();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Unexpected IOException", ex);
|
||||
}
|
||||
}
|
||||
MockPart part = new MockPart(file.getName(), file.getOriginalFilename(), bytes);
|
||||
if (file.getContentType() != null) {
|
||||
part.getHeaders().set(HttpHeaders.CONTENT_TYPE, file.getContentType());
|
||||
}
|
||||
return part;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ class ModelResultMatchersDsl internal constructor (private val actions: ResultAc
|
||||
/**
|
||||
* @see ModelResultMatchers.attributeErrorCount
|
||||
*/
|
||||
fun <T> attributeErrorCount(name: String, expectedCount: Int) {
|
||||
fun attributeErrorCount(name: String, expectedCount: Int) {
|
||||
actions.andExpect(matchers.attributeErrorCount(name, expectedCount))
|
||||
}
|
||||
|
||||
|
||||
+308
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* 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.test.context.junit.jupiter;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.assertj.core.api.Condition;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.RepeatedTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.junit.platform.testkit.engine.EngineTestKit;
|
||||
import org.junit.platform.testkit.engine.Event;
|
||||
import org.junit.platform.testkit.engine.EventConditions;
|
||||
import org.junit.platform.testkit.engine.Events;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.allOf;
|
||||
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
||||
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
|
||||
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
|
||||
import static org.junit.platform.testkit.engine.EventConditions.container;
|
||||
import static org.junit.platform.testkit.engine.EventConditions.event;
|
||||
import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;
|
||||
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;
|
||||
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link Autowired @Autowired} configuration errors in
|
||||
* JUnit Jupiter test classes.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 5.3.2
|
||||
*/
|
||||
class AutowiredConfigurationErrorsIntegrationTests {
|
||||
|
||||
private static final String DISPLAY_NAME = "TEST";
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(classes = {
|
||||
StaticAutowiredBeforeAllMethod.class,
|
||||
StaticAutowiredAfterAllMethod.class,
|
||||
AutowiredBeforeEachMethod.class,
|
||||
AutowiredAfterEachMethod.class,
|
||||
AutowiredTestMethod.class,
|
||||
AutowiredRepeatedTestMethod.class,
|
||||
AutowiredParameterizedTestMethod.class
|
||||
})
|
||||
void autowiredTestMethodsTestTemplateMethodsAndLifecyleMethods(Class<?> testClass) {
|
||||
testEventsFor(testClass)
|
||||
.assertStatistics(stats -> stats.started(1).succeeded(0).failed(1))
|
||||
.assertThatEvents().haveExactly(1,
|
||||
event(testWithDisplayName(DISPLAY_NAME),
|
||||
finishedWithFailure(
|
||||
instanceOf(IllegalStateException.class),
|
||||
message(msg -> msg.matches(".+must not be annotated with @Autowired.+")))));
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-autowired test method should fail the same as an autowired test
|
||||
* method in the same class, since Spring still should not autowire the
|
||||
* autowired test method as a "configuration method" when JUnit attempts to
|
||||
* execute the non-autowired test method.
|
||||
*/
|
||||
@Test
|
||||
void autowiredAndNonAutowiredTestMethods() {
|
||||
testEventsFor(AutowiredAndNonAutowiredTestMethods.class)
|
||||
.assertStatistics(stats -> stats.started(2).succeeded(0).failed(2))
|
||||
.assertThatEvents()
|
||||
.haveExactly(1,
|
||||
event(testWithDisplayName("autowired(TestInfo)"),
|
||||
finishedWithFailure(
|
||||
instanceOf(IllegalStateException.class),
|
||||
message(msg -> msg.matches(".+must not be annotated with @Autowired.+")))))
|
||||
.haveExactly(1,
|
||||
event(testWithDisplayName("nonAutowired(TestInfo)"),
|
||||
finishedWithFailure(
|
||||
instanceOf(IllegalStateException.class),
|
||||
message(msg -> msg.matches(".+must not be annotated with @Autowired.+")))));
|
||||
}
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(classes = {
|
||||
NonStaticAutowiredBeforeAllMethod.class,
|
||||
NonStaticAutowiredAfterAllMethod.class
|
||||
})
|
||||
void autowiredNonStaticClassLevelLifecyleMethods(Class<?> testClass) {
|
||||
containerEventsFor(testClass)
|
||||
.assertStatistics(stats -> stats.started(2).succeeded(1).failed(1))
|
||||
.assertThatEvents().haveExactly(1,
|
||||
event(container(),
|
||||
finishedWithFailure(
|
||||
instanceOf(IllegalStateException.class),
|
||||
message(msg -> msg.matches(".+must not be annotated with @Autowired.+")))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autowiredTestFactoryMethod() {
|
||||
containerEventsFor(AutowiredTestFactoryMethod.class)
|
||||
.assertStatistics(stats -> stats.started(3).succeeded(2).failed(1))
|
||||
.assertThatEvents().haveExactly(1,
|
||||
event(container(),
|
||||
finishedWithFailure(
|
||||
instanceOf(IllegalStateException.class),
|
||||
message(msg -> msg.matches(".+must not be annotated with @Autowired.+")))));
|
||||
}
|
||||
|
||||
private Events testEventsFor(Class<?> testClass) {
|
||||
return EngineTestKit.engine("junit-jupiter")
|
||||
.selectors(selectClass(testClass))
|
||||
.execute()
|
||||
.testEvents();
|
||||
}
|
||||
|
||||
private Events containerEventsFor(Class<?> testClass) {
|
||||
return EngineTestKit.engine("junit-jupiter")
|
||||
.selectors(selectClass(testClass))
|
||||
.execute()
|
||||
.containerEvents();
|
||||
}
|
||||
|
||||
private static Condition<Event> testWithDisplayName(String displayName) {
|
||||
return allOf(EventConditions.test(), EventConditions.displayName(displayName));
|
||||
}
|
||||
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@FailingTestCase
|
||||
static class StaticAutowiredBeforeAllMethod {
|
||||
|
||||
@Autowired
|
||||
@BeforeAll
|
||||
static void beforeAll(TestInfo testInfo) {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(DISPLAY_NAME)
|
||||
void test() {
|
||||
}
|
||||
}
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@TestInstance(PER_CLASS)
|
||||
@FailingTestCase
|
||||
static class NonStaticAutowiredBeforeAllMethod {
|
||||
|
||||
@Autowired
|
||||
@BeforeAll
|
||||
void beforeAll(TestInfo testInfo) {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(DISPLAY_NAME)
|
||||
void test() {
|
||||
}
|
||||
}
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@FailingTestCase
|
||||
static class StaticAutowiredAfterAllMethod {
|
||||
|
||||
@Test
|
||||
@DisplayName(DISPLAY_NAME)
|
||||
void test() {
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
@Autowired
|
||||
static void afterAll(TestInfo testInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@TestInstance(PER_CLASS)
|
||||
@FailingTestCase
|
||||
static class NonStaticAutowiredAfterAllMethod {
|
||||
|
||||
@Test
|
||||
@DisplayName(DISPLAY_NAME)
|
||||
void test() {
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
@Autowired
|
||||
void afterAll(TestInfo testInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@FailingTestCase
|
||||
static class AutowiredBeforeEachMethod {
|
||||
|
||||
@Autowired
|
||||
@BeforeEach
|
||||
void beforeEach(TestInfo testInfo) {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName(DISPLAY_NAME)
|
||||
void test() {
|
||||
}
|
||||
}
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@FailingTestCase
|
||||
static class AutowiredAfterEachMethod {
|
||||
|
||||
@Test
|
||||
@DisplayName(DISPLAY_NAME)
|
||||
void test() {
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@AfterEach
|
||||
void afterEach(TestInfo testInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@FailingTestCase
|
||||
static class AutowiredTestMethod {
|
||||
|
||||
@Autowired
|
||||
@Test
|
||||
@DisplayName(DISPLAY_NAME)
|
||||
void test(TestInfo testInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@FailingTestCase
|
||||
static class AutowiredAndNonAutowiredTestMethods {
|
||||
|
||||
@Autowired
|
||||
@Test
|
||||
void autowired(TestInfo testInfo) {
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonAutowired(TestInfo testInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@FailingTestCase
|
||||
static class AutowiredRepeatedTestMethod {
|
||||
|
||||
@Autowired
|
||||
@RepeatedTest(value = 1, name = DISPLAY_NAME)
|
||||
void test(TestInfo testInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@FailingTestCase
|
||||
static class AutowiredTestFactoryMethod {
|
||||
|
||||
@Autowired
|
||||
@TestFactory
|
||||
Stream<DynamicTest> testFactory(TestInfo testInfo) {
|
||||
return Stream.of(dynamicTest("dynamicTest", () -> {}));
|
||||
}
|
||||
}
|
||||
|
||||
@SpringJUnitConfig(Config.class)
|
||||
@FailingTestCase
|
||||
static class AutowiredParameterizedTestMethod {
|
||||
|
||||
@Autowired
|
||||
@ParameterizedTest(name = DISPLAY_NAME)
|
||||
@ValueSource(strings = "ignored")
|
||||
void test(TestInfo testInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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.test.context.junit.jupiter.nested;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.context.DynamicPropertyRegistry;
|
||||
import org.springframework.test.context.DynamicPropertySource;
|
||||
import org.springframework.test.context.NestedTestConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.context.NestedTestConfiguration.EnclosingConfiguration.OVERRIDE;
|
||||
|
||||
/**
|
||||
* Integration tests that verify support for {@code @Nested} test classes using
|
||||
* {@link DynamicPropertySource @DynamicPropertySource} in conjunction with the
|
||||
* {@link SpringExtension} in a JUnit Jupiter environment.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 5.3.2
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
class DynamicPropertySourceNestedTests {
|
||||
|
||||
private static final String TEST_CONTAINER_IP = "DynamicPropertySourceNestedTests.test.container.ip";
|
||||
|
||||
private static final String TEST_CONTAINER_PORT = "DynamicPropertySourceNestedTests.test.container.port";
|
||||
|
||||
static DemoContainer container = new DemoContainer();
|
||||
|
||||
@DynamicPropertySource
|
||||
static void containerProperties(DynamicPropertyRegistry registry) {
|
||||
registry.add(TEST_CONTAINER_IP, container::getIpAddress);
|
||||
registry.add(TEST_CONTAINER_PORT, container::getPort);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@DisplayName("@Service has values injected from @DynamicPropertySource")
|
||||
void serviceHasInjectedValues(@Autowired Service service) {
|
||||
assertServiceHasInjectedValues(service);
|
||||
}
|
||||
|
||||
private static void assertServiceHasInjectedValues(Service service) {
|
||||
assertThat(service.getIp()).isEqualTo("127.0.0.1");
|
||||
assertThat(service.getPort()).isEqualTo(4242);
|
||||
}
|
||||
|
||||
@Nested
|
||||
@NestedTestConfiguration(OVERRIDE)
|
||||
@SpringJUnitConfig(Config.class)
|
||||
class DynamicPropertySourceFromSuperclassTests extends DynamicPropertySourceSuperclass {
|
||||
|
||||
@Test
|
||||
@DisplayName("@Service has values injected from @DynamicPropertySource in superclass")
|
||||
void serviceHasInjectedValues(@Autowired Service service) {
|
||||
assertServiceHasInjectedValues(service);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@NestedTestConfiguration(OVERRIDE)
|
||||
@SpringJUnitConfig(Config.class)
|
||||
class DynamicPropertySourceFromInterfaceTests implements DynamicPropertySourceInterface {
|
||||
|
||||
@Test
|
||||
@DisplayName("@Service has values injected from @DynamicPropertySource in interface")
|
||||
void serviceHasInjectedValues(@Autowired Service service) {
|
||||
assertServiceHasInjectedValues(service);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@NestedTestConfiguration(OVERRIDE)
|
||||
@SpringJUnitConfig(Config.class)
|
||||
class OverriddenConfigTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("@Service does not have values injected from @DynamicPropertySource in enclosing class")
|
||||
void serviceHasDefaultInjectedValues(@Autowired Service service) {
|
||||
assertThat(service.getIp()).isEqualTo("10.0.0.1");
|
||||
assertThat(service.getPort()).isEqualTo(-999);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class DynamicPropertySourceFromEnclosingClassTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("@Service has values injected from @DynamicPropertySource in enclosing class")
|
||||
void serviceHasInjectedValues(@Autowired Service service) {
|
||||
assertServiceHasInjectedValues(service);
|
||||
}
|
||||
|
||||
@Nested
|
||||
class DoubleNestedDynamicPropertySourceFromEnclosingClassTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("@Service has values injected from @DynamicPropertySource in enclosing class")
|
||||
void serviceHasInjectedValues(@Autowired Service service) {
|
||||
assertServiceHasInjectedValues(service);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static abstract class DynamicPropertySourceSuperclass {
|
||||
|
||||
@DynamicPropertySource
|
||||
static void containerProperties(DynamicPropertyRegistry registry) {
|
||||
registry.add(TEST_CONTAINER_IP, container::getIpAddress);
|
||||
registry.add(TEST_CONTAINER_PORT, container::getPort);
|
||||
}
|
||||
}
|
||||
|
||||
interface DynamicPropertySourceInterface {
|
||||
|
||||
@DynamicPropertySource
|
||||
static void containerProperties(DynamicPropertyRegistry registry) {
|
||||
registry.add(TEST_CONTAINER_IP, container::getIpAddress);
|
||||
registry.add(TEST_CONTAINER_PORT, container::getPort);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@Import(Service.class)
|
||||
static class Config {
|
||||
}
|
||||
|
||||
static class Service {
|
||||
|
||||
private final String ip;
|
||||
|
||||
private final int port;
|
||||
|
||||
|
||||
Service(@Value("${" + TEST_CONTAINER_IP + ":10.0.0.1}") String ip, @Value("${" + TEST_CONTAINER_PORT + ":-999}") int port) {
|
||||
this.ip = ip;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
String getIp() {
|
||||
return this.ip;
|
||||
}
|
||||
|
||||
int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
}
|
||||
|
||||
static class DemoContainer {
|
||||
|
||||
String getIpAddress() {
|
||||
return "127.0.0.1";
|
||||
}
|
||||
|
||||
int getPort() {
|
||||
return 4242;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+24
-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.
|
||||
@@ -16,21 +16,43 @@
|
||||
|
||||
package org.springframework.test.web.servlet.request;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import javax.servlet.http.Part;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.mock.web.MockPart;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link MockMultipartHttpServletRequestBuilder}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class MockMultipartHttpServletRequestBuilderTests {
|
||||
|
||||
@Test // gh-26166
|
||||
void addFilesAndParts() throws Exception {
|
||||
MockHttpServletRequest mockRequest = new MockMultipartHttpServletRequestBuilder("/upload")
|
||||
.file(new MockMultipartFile("file", "test.txt", "text/plain", "Test".getBytes(StandardCharsets.UTF_8)))
|
||||
.part(new MockPart("data", "{\"node\":\"node\"}".getBytes(StandardCharsets.UTF_8)))
|
||||
.buildRequest(new MockServletContext());
|
||||
|
||||
StandardMultipartHttpServletRequest parsedRequest = new StandardMultipartHttpServletRequest(mockRequest);
|
||||
|
||||
assertThat(parsedRequest.getParameterMap()).containsOnlyKeys("data");
|
||||
assertThat(parsedRequest.getFileMap()).containsOnlyKeys("file");
|
||||
assertThat(parsedRequest.getParts()).extracting(Part::getName).containsExactly("file", "data");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
void mergeAndBuild() {
|
||||
MockHttpServletRequestBuilder parent = new MockHttpServletRequestBuilder(HttpMethod.GET, "/");
|
||||
parent.characterEncoding("UTF-8");
|
||||
Object result = new MockMultipartHttpServletRequestBuilder("/fileUpload").merge(parent);
|
||||
|
||||
+14
-5
@@ -38,16 +38,25 @@ import org.springframework.transaction.TransactionDefinition;
|
||||
* {@link org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}
|
||||
* class, and in fact {@link AnnotationTransactionAttributeSource} will directly
|
||||
* convert the data to the latter class, so that Spring's transaction support code
|
||||
* does not have to know about annotations. If no rules are relevant to the exception,
|
||||
* it will be treated like
|
||||
* {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute}
|
||||
* (rolling back on {@link RuntimeException} and {@link Error} but not on checked
|
||||
* exceptions).
|
||||
* does not have to know about annotations. If no custom rollback rules apply,
|
||||
* the transaction will roll back on {@link RuntimeException} and {@link Error}
|
||||
* but not on checked exceptions.
|
||||
*
|
||||
* <p>For specific information about the semantics of this annotation's attributes,
|
||||
* consult the {@link org.springframework.transaction.TransactionDefinition} and
|
||||
* {@link org.springframework.transaction.interceptor.TransactionAttribute} javadocs.
|
||||
*
|
||||
* <p>This annotation commonly works with thread-bound transactions managed by
|
||||
* {@link org.springframework.transaction.PlatformTransactionManager}, exposing a
|
||||
* transaction to all data access operations within the current execution thread.
|
||||
* <b>Note: This does NOT propagate to newly started threads within the method.</b>
|
||||
*
|
||||
* <p>Alternatively, this annotation may demarcate a reactive transaction managed
|
||||
* by {@link org.springframework.transaction.ReactiveTransactionManager} which
|
||||
* uses the Reactor context instead of thread-local attributes. As a consequence,
|
||||
* all participating data access operations need to execute within the same
|
||||
* Reactor context in the same reactive pipeline.
|
||||
*
|
||||
* @author Colin Sampaleanu
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ import org.springframework.lang.Nullable;
|
||||
/**
|
||||
* TransactionAttribute implementation that works out whether a given exception
|
||||
* should cause transaction rollback by applying a number of rollback rules,
|
||||
* both positive and negative. If no rules are relevant to the exception, it
|
||||
* both positive and negative. If no custom rollback rules apply, this attribute
|
||||
* behaves like DefaultTransactionAttribute (rolling back on runtime exceptions).
|
||||
*
|
||||
* <p>{@link TransactionAttributeEditor} creates objects of this class.
|
||||
|
||||
+37
-7
@@ -34,6 +34,7 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
|
||||
import org.springframework.core.CoroutinesUtils;
|
||||
import org.springframework.core.KotlinDetector;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
@@ -342,9 +343,16 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
|
||||
|
||||
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
|
||||
boolean isSuspendingFunction = KotlinDetector.isSuspendingFunction(method);
|
||||
boolean hasSuspendingFlowReturnType = isSuspendingFunction && COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName());
|
||||
boolean hasSuspendingFlowReturnType = isSuspendingFunction &&
|
||||
COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName());
|
||||
if (isSuspendingFunction && !(invocation instanceof CoroutinesInvocationCallback)) {
|
||||
throw new IllegalStateException("Coroutines invocation not supported: " + method);
|
||||
}
|
||||
CoroutinesInvocationCallback corInv = (isSuspendingFunction ? (CoroutinesInvocationCallback) invocation : null);
|
||||
|
||||
ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
|
||||
Class<?> reactiveType = (isSuspendingFunction ? (hasSuspendingFlowReturnType ? Flux.class : Mono.class) : method.getReturnType());
|
||||
Class<?> reactiveType =
|
||||
(isSuspendingFunction ? (hasSuspendingFlowReturnType ? Flux.class : Mono.class) : method.getReturnType());
|
||||
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(reactiveType);
|
||||
if (adapter == null) {
|
||||
throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
|
||||
@@ -352,9 +360,18 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
|
||||
}
|
||||
return new ReactiveTransactionSupport(adapter);
|
||||
});
|
||||
Object result = txSupport.invokeWithinTransaction(method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);
|
||||
return (isSuspendingFunction ? (hasSuspendingFlowReturnType ? KotlinDelegate.asFlow((Publisher<?>) result) :
|
||||
KotlinDelegate.awaitSingleOrNull((Publisher<?>) result, ((CoroutinesInvocationCallback) invocation).getContinuation())) : result);
|
||||
|
||||
InvocationCallback callback = invocation;
|
||||
if (corInv != null) {
|
||||
callback = () -> CoroutinesUtils.invokeSuspendingFunction(method, corInv.getTarget(), corInv.getArguments());
|
||||
}
|
||||
Object result = txSupport.invokeWithinTransaction(method, targetClass, callback, txAttr, (ReactiveTransactionManager) tm);
|
||||
if (corInv != null) {
|
||||
Publisher<?> pr = (Publisher<?>) result;
|
||||
return (hasSuspendingFlowReturnType ? KotlinDelegate.asFlow(pr) :
|
||||
KotlinDelegate.awaitSingleOrNull(pr, corInv.getContinuation()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
|
||||
@@ -789,9 +806,20 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
|
||||
Object proceedWithInvocation() throws Throwable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Coroutines-supporting extension of the callback interface.
|
||||
*/
|
||||
protected interface CoroutinesInvocationCallback extends InvocationCallback {
|
||||
|
||||
Object getContinuation();
|
||||
Object getTarget();
|
||||
|
||||
Object[] getArguments();
|
||||
|
||||
default Object getContinuation() {
|
||||
Object[] args = getArguments();
|
||||
return args[args.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -876,7 +904,9 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
|
||||
String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
|
||||
|
||||
// For Mono and suspending functions not returning kotlinx.coroutines.flow.Flow
|
||||
if (Mono.class.isAssignableFrom(method.getReturnType()) || (KotlinDetector.isSuspendingFunction(method) && !COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName()))) {
|
||||
if (Mono.class.isAssignableFrom(method.getReturnType()) || (KotlinDetector.isSuspendingFunction(method) &&
|
||||
!COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName()))) {
|
||||
|
||||
return TransactionContextManager.currentContext().flatMap(context ->
|
||||
createTransactionIfNecessary(rtm, txAttr, joinpointIdentification).flatMap(it -> {
|
||||
try {
|
||||
|
||||
+15
-16
@@ -27,8 +27,6 @@ import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.CoroutinesUtils;
|
||||
import org.springframework.core.KotlinDetector;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionManager;
|
||||
@@ -118,20 +116,21 @@ public class TransactionInterceptor extends TransactionAspectSupport implements
|
||||
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
|
||||
|
||||
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
|
||||
if (KotlinDetector.isSuspendingFunction(invocation.getMethod())) {
|
||||
InvocationCallback callback = new CoroutinesInvocationCallback() {
|
||||
@Override
|
||||
public Object proceedWithInvocation() {
|
||||
return CoroutinesUtils.invokeSuspendingFunction(invocation.getMethod(), invocation.getThis(), invocation.getArguments());
|
||||
}
|
||||
@Override
|
||||
public Object getContinuation() {
|
||||
return invocation.getArguments()[invocation.getArguments().length - 1];
|
||||
}
|
||||
};
|
||||
return invokeWithinTransaction(invocation.getMethod(), targetClass, callback);
|
||||
}
|
||||
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
|
||||
return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
|
||||
@Override
|
||||
@Nullable
|
||||
public Object proceedWithInvocation() throws Throwable {
|
||||
return invocation.proceed();
|
||||
}
|
||||
@Override
|
||||
public Object getTarget() {
|
||||
return invocation.getThis();
|
||||
}
|
||||
@Override
|
||||
public Object[] getArguments() {
|
||||
return invocation.getArguments();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -485,7 +485,8 @@ public final class ContentDisposition {
|
||||
}
|
||||
boolean escaped = false;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (char c : filename.toCharArray()) {
|
||||
for (int i = 0; i < filename.length() ; i++) {
|
||||
char c = filename.charAt(i);
|
||||
if (!escaped && c == '"') {
|
||||
sb.append("\\\"");
|
||||
}
|
||||
|
||||
@@ -514,9 +514,9 @@ public class MediaType extends MimeType implements Serializable {
|
||||
|
||||
|
||||
@Override
|
||||
protected void checkParameters(String attribute, String value) {
|
||||
super.checkParameters(attribute, value);
|
||||
if (PARAM_QUALITY_FACTOR.equals(attribute)) {
|
||||
protected void checkParameters(String parameter, String value) {
|
||||
super.checkParameters(parameter, value);
|
||||
if (PARAM_QUALITY_FACTOR.equals(parameter)) {
|
||||
value = unquote(value);
|
||||
double d = Double.parseDouble(value);
|
||||
Assert.isTrue(d >= 0D && d <= 1D,
|
||||
|
||||
@@ -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.
|
||||
@@ -112,10 +112,12 @@ public final class MediaTypeFactory {
|
||||
* @return the corresponding media types, or an empty list if none found
|
||||
*/
|
||||
public static List<MediaType> getMediaTypes(@Nullable String filename) {
|
||||
return Optional.ofNullable(StringUtils.getFilenameExtension(filename))
|
||||
.map(s -> s.toLowerCase(Locale.ENGLISH))
|
||||
.map(fileExtensionToMediaTypes::get)
|
||||
.orElse(Collections.emptyList());
|
||||
List<MediaType> mediaTypes = null;
|
||||
String ext = StringUtils.getFilenameExtension(filename);
|
||||
if (ext != null) {
|
||||
mediaTypes = fileExtensionToMediaTypes.get(ext.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
return (mediaTypes != null ? mediaTypes : Collections.emptyList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -386,9 +386,8 @@ public final class ResponseCookie extends HttpCookie {
|
||||
start = 1;
|
||||
end--;
|
||||
}
|
||||
char[] chars = value.toCharArray();
|
||||
for (int i = start; i < end; i++) {
|
||||
char c = chars[i];
|
||||
char c = value.charAt(i);
|
||||
if (c < 0x21 || c == 0x22 || c == 0x2c || c == 0x3b || c == 0x5c || c == 0x7f) {
|
||||
throw new IllegalArgumentException(
|
||||
"RFC2616 cookie value cannot have '" + c + "'");
|
||||
|
||||
@@ -113,9 +113,18 @@ public class ResponseEntity<T> extends HttpEntity<T> {
|
||||
* @param status the status code
|
||||
*/
|
||||
public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatus status) {
|
||||
super(body, headers);
|
||||
Assert.notNull(status, "HttpStatus must not be null");
|
||||
this.status = status;
|
||||
this(body, headers, (Object) status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code HttpEntity} with the given body, headers, and status code.
|
||||
* @param body the entity body
|
||||
* @param headers the entity headers
|
||||
* @param rawStatus the status code value
|
||||
* @since 5.3.2
|
||||
*/
|
||||
public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, int rawStatus) {
|
||||
this(body, headers, (Object) rawStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+16
-3
@@ -57,6 +57,9 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
|
||||
|
||||
private static final Log logger = HttpLogging.forLogName(EncoderHttpMessageWriter.class);
|
||||
|
||||
|
||||
private final Encoder<T> encoder;
|
||||
|
||||
private final List<MediaType> mediaTypes;
|
||||
@@ -125,17 +128,24 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
|
||||
return message.setComplete().then(Mono.empty());
|
||||
}))
|
||||
.flatMap(buffer -> {
|
||||
Hints.touchDataBuffer(buffer, hints, logger);
|
||||
message.getHeaders().setContentLength(buffer.readableByteCount());
|
||||
return message.writeWith(Mono.just(buffer)
|
||||
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release));
|
||||
});
|
||||
})
|
||||
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
|
||||
}
|
||||
|
||||
if (isStreamingMediaType(contentType)) {
|
||||
return message.writeAndFlushWith(body.map(buffer ->
|
||||
Mono.just(buffer).doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release)));
|
||||
return message.writeAndFlushWith(body.map(buffer -> {
|
||||
Hints.touchDataBuffer(buffer, hints, logger);
|
||||
return Mono.just(buffer).doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
|
||||
}));
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
body = body.doOnNext(buffer -> Hints.touchDataBuffer(buffer, hints, logger));
|
||||
}
|
||||
return message.writeWith(body);
|
||||
}
|
||||
|
||||
@@ -166,6 +176,9 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
|
||||
return main;
|
||||
}
|
||||
|
||||
private static void touch(DataBuffer buffer, Map<String, Object> hints) {
|
||||
}
|
||||
|
||||
private boolean isStreamingMediaType(@Nullable MediaType mediaType) {
|
||||
if (mediaType == null || !(this.encoder instanceof HttpMessageEncoder)) {
|
||||
return false;
|
||||
|
||||
+4
-1
@@ -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.
|
||||
@@ -132,6 +132,9 @@ public class ResourceHttpMessageWriter implements HttpMessageWriter<Resource> {
|
||||
Mono<Resource> input = Mono.just(resource);
|
||||
DataBufferFactory factory = message.bufferFactory();
|
||||
Flux<DataBuffer> body = this.encoder.encode(input, factory, type, resourceMediaType, hints);
|
||||
if (logger.isDebugEnabled()) {
|
||||
body = body.doOnNext(buffer -> Hints.touchDataBuffer(buffer, hints, logger));
|
||||
}
|
||||
return message.writeWith(body);
|
||||
});
|
||||
}
|
||||
|
||||
+7
-1
@@ -23,6 +23,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -35,6 +36,7 @@ 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.PooledDataBuffer;
|
||||
import org.springframework.http.HttpLogging;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ReactiveHttpOutputMessage;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
@@ -57,6 +59,8 @@ public class ServerSentEventHttpMessageWriter implements HttpMessageWriter<Objec
|
||||
|
||||
private static final List<MediaType> WRITABLE_MEDIA_TYPES = Collections.singletonList(MediaType.TEXT_EVENT_STREAM);
|
||||
|
||||
private static final Log logger = HttpLogging.forLogName(ServerSentEventHttpMessageWriter.class);
|
||||
|
||||
|
||||
@Nullable
|
||||
private final Encoder<?> encoder;
|
||||
@@ -167,9 +171,11 @@ public class ServerSentEventHttpMessageWriter implements HttpMessageWriter<Objec
|
||||
if (this.encoder == null) {
|
||||
throw new CodecException("No SSE encoder configured and the data is not String.");
|
||||
}
|
||||
DataBuffer buffer = ((Encoder<T>) this.encoder).encodeValue(data, factory, dataType, mediaType, hints);
|
||||
Hints.touchDataBuffer(buffer, hints, logger);
|
||||
return Flux.just(factory.join(Arrays.asList(
|
||||
encodeText(eventContent, mediaType, factory),
|
||||
((Encoder<T>) this.encoder).encodeValue(data, factory, dataType, mediaType, hints),
|
||||
buffer,
|
||||
encodeText("\n\n", mediaType, factory))));
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -223,6 +223,7 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
||||
byte[] bytes = byteBuilder.toByteArray();
|
||||
DataBuffer buffer = bufferFactory.allocateBuffer(bytes.length);
|
||||
buffer.write(bytes);
|
||||
Hints.touchDataBuffer(buffer, hints, logger);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
@@ -267,6 +268,7 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
|
||||
DataBuffer buffer = bufferFactory.allocateBuffer(length + separator.length);
|
||||
buffer.write(bytes, offset, length);
|
||||
buffer.write(separator);
|
||||
Hints.touchDataBuffer(buffer, hints, logger);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
+30
-1
@@ -69,10 +69,38 @@ public class KotlinSerializationJsonDecoder extends AbstractDecoder<Object> {
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a limit on the number of bytes that can be buffered whenever
|
||||
* the input stream needs to be aggregated. This can be a result of
|
||||
* decoding to a single {@code DataBuffer},
|
||||
* {@link java.nio.ByteBuffer ByteBuffer}, {@code byte[]},
|
||||
* {@link org.springframework.core.io.Resource Resource}, {@code String}, etc.
|
||||
* It can also occur when splitting the input stream, e.g. delimited text,
|
||||
* in which case the limit applies to data buffered between delimiters.
|
||||
* <p>By default this is set to 256K.
|
||||
* @param byteCount the max number of bytes to buffer, or -1 for unlimited
|
||||
*/
|
||||
public void setMaxInMemorySize(int byteCount) {
|
||||
this.stringDecoder.setMaxInMemorySize(byteCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link #setMaxInMemorySize configured} byte count limit.
|
||||
*/
|
||||
public int getMaxInMemorySize() {
|
||||
return this.stringDecoder.getMaxInMemorySize();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canDecode(ResolvableType elementType, @Nullable MimeType mimeType) {
|
||||
return (super.canDecode(elementType, mimeType) && !CharSequence.class.isAssignableFrom(elementType.toClass()));
|
||||
try {
|
||||
serializer(elementType.getType());
|
||||
return (super.canDecode(elementType, mimeType) && !CharSequence.class.isAssignableFrom(elementType.toClass()));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,6 +123,7 @@ public class KotlinSerializationJsonDecoder extends AbstractDecoder<Object> {
|
||||
* Tries to find a serializer that can marshall or unmarshall instances of the given type
|
||||
* using kotlinx.serialization. If no serializer can be found, an exception is thrown.
|
||||
* <p>Resolved serializers are cached and cached results are returned on successive calls.
|
||||
* TODO Avoid relying on throwing exception when https://github.com/Kotlin/kotlinx.serialization/pull/1164 is fixed
|
||||
* @param type the type to find a serializer for
|
||||
* @return a resolved serializer for the given type
|
||||
* @throws RuntimeException if no serializer supporting the given type can be found
|
||||
|
||||
+9
-2
@@ -71,8 +71,14 @@ public class KotlinSerializationJsonEncoder extends AbstractEncoder<Object> {
|
||||
|
||||
@Override
|
||||
public boolean canEncode(ResolvableType elementType, @Nullable MimeType mimeType) {
|
||||
return (super.canEncode(elementType, mimeType) && !String.class.isAssignableFrom(elementType.toClass()) &&
|
||||
!ServerSentEvent.class.isAssignableFrom(elementType.toClass()));
|
||||
try {
|
||||
serializer(elementType.getType());
|
||||
return (super.canEncode(elementType, mimeType) && !String.class.isAssignableFrom(elementType.toClass()) &&
|
||||
!ServerSentEvent.class.isAssignableFrom(elementType.toClass()));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,6 +111,7 @@ public class KotlinSerializationJsonEncoder extends AbstractEncoder<Object> {
|
||||
* Tries to find a serializer that can marshall or unmarshall instances of the given type
|
||||
* using kotlinx.serialization. If no serializer can be found, an exception is thrown.
|
||||
* <p>Resolved serializers are cached and cached results are returned on successive calls.
|
||||
* TODO Avoid relying on throwing exception when https://github.com/Kotlin/kotlinx.serialization/pull/1164 is fixed
|
||||
* @param type the type to find a serializer for
|
||||
* @return a resolved serializer for the given type
|
||||
* @throws RuntimeException if no serializer supporting the given type can be found
|
||||
|
||||
+4
-10
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.http.codec.multipart;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -146,15 +145,6 @@ public class MultipartHttpMessageWriter extends MultipartWriterSupport
|
||||
return this.formWriter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the character set to use for part headers such as
|
||||
* "Content-Disposition" (and its filename parameter).
|
||||
* <p>By default this is set to "UTF-8".
|
||||
*/
|
||||
public void setCharset(Charset charset) {
|
||||
Assert.notNull(charset, "Charset must not be null");
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@@ -209,6 +199,10 @@ public class MultipartHttpMessageWriter extends MultipartWriterSupport
|
||||
.concatWith(generateLastLine(boundary, bufferFactory))
|
||||
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
body = body.doOnNext(buffer -> Hints.touchDataBuffer(buffer, hints, logger));
|
||||
}
|
||||
|
||||
return outputMessage.writeWith(body);
|
||||
}
|
||||
|
||||
|
||||
+20
-3
@@ -30,6 +30,7 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.LoggingCodecSupport;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
@@ -44,9 +45,9 @@ public class MultipartWriterSupport extends LoggingCodecSupport {
|
||||
/** THe default charset used by the writer. */
|
||||
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
protected final List<MediaType> supportedMediaTypes;
|
||||
private final List<MediaType> supportedMediaTypes;
|
||||
|
||||
protected Charset charset = DEFAULT_CHARSET;
|
||||
private Charset charset = DEFAULT_CHARSET;
|
||||
|
||||
|
||||
/**
|
||||
@@ -64,6 +65,18 @@ public class MultipartWriterSupport extends LoggingCodecSupport {
|
||||
return this.charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the character set to use for part headers such as
|
||||
* "Content-Disposition" (and its filename parameter).
|
||||
* <p>By default this is set to "UTF-8". If changed from this default,
|
||||
* the "Content-Type" header will have a "charset" parameter that specifies
|
||||
* the character set used.
|
||||
*/
|
||||
public void setCharset(Charset charset) {
|
||||
Assert.notNull(charset, "Charset must not be null");
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
public List<MediaType> getWritableMediaTypes() {
|
||||
return this.supportedMediaTypes;
|
||||
}
|
||||
@@ -102,7 +115,11 @@ public class MultipartWriterSupport extends LoggingCodecSupport {
|
||||
params.putAll(mediaType.getParameters());
|
||||
}
|
||||
params.put("boundary", new String(boundary, StandardCharsets.US_ASCII));
|
||||
params.put("charset", getCharset().name());
|
||||
Charset charset = getCharset();
|
||||
if (!charset.equals(StandardCharsets.UTF_8) &&
|
||||
!charset.equals(StandardCharsets.US_ASCII) ) {
|
||||
params.put("charset", getCharset().name());
|
||||
}
|
||||
|
||||
mediaType = (mediaType != null ? mediaType : MediaType.MULTIPART_FORM_DATA);
|
||||
mediaType = new MediaType(mediaType, params);
|
||||
|
||||
+4
@@ -69,6 +69,10 @@ public class PartHttpMessageWriter extends MultipartWriterSupport implements Htt
|
||||
.concatWith(generateLastLine(boundary, outputMessage.bufferFactory()))
|
||||
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
body = body.doOnNext(buffer -> Hints.touchDataBuffer(buffer, hints, logger));
|
||||
}
|
||||
|
||||
return outputMessage.writeWith(body);
|
||||
}
|
||||
|
||||
|
||||
+11
-6
@@ -312,6 +312,11 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure
|
||||
((ProtobufDecoder) codec).setMaxMessageSize(size);
|
||||
}
|
||||
}
|
||||
if (kotlinSerializationJsonPresent) {
|
||||
if (codec instanceof KotlinSerializationJsonDecoder) {
|
||||
((KotlinSerializationJsonDecoder) codec).setMaxInMemorySize(size);
|
||||
}
|
||||
}
|
||||
if (jackson2Present) {
|
||||
if (codec instanceof AbstractJackson2Decoder) {
|
||||
((AbstractJackson2Decoder) codec).setMaxInMemorySize(size);
|
||||
@@ -385,12 +390,12 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<HttpMessageReader<?>> readers = new ArrayList<>();
|
||||
if (kotlinSerializationJsonPresent) {
|
||||
addCodec(readers, new DecoderHttpMessageReader<>(getKotlinSerializationJsonDecoder()));
|
||||
}
|
||||
if (jackson2Present) {
|
||||
addCodec(readers, new DecoderHttpMessageReader<>(getJackson2JsonDecoder()));
|
||||
}
|
||||
else if (kotlinSerializationJsonPresent) {
|
||||
addCodec(readers, new DecoderHttpMessageReader<>(getKotlinSerializationJsonDecoder()));
|
||||
}
|
||||
if (jackson2SmilePresent) {
|
||||
addCodec(readers, new DecoderHttpMessageReader<>(this.jackson2SmileDecoder != null ?
|
||||
(Jackson2SmileDecoder) this.jackson2SmileDecoder : new Jackson2SmileDecoder()));
|
||||
@@ -484,12 +489,12 @@ class BaseDefaultCodecs implements CodecConfigurer.DefaultCodecs, CodecConfigure
|
||||
*/
|
||||
final List<HttpMessageWriter<?>> getBaseObjectWriters() {
|
||||
List<HttpMessageWriter<?>> writers = new ArrayList<>();
|
||||
if (kotlinSerializationJsonPresent) {
|
||||
writers.add(new EncoderHttpMessageWriter<>(getKotlinSerializationJsonEncoder()));
|
||||
}
|
||||
if (jackson2Present) {
|
||||
writers.add(new EncoderHttpMessageWriter<>(getJackson2JsonEncoder()));
|
||||
}
|
||||
else if (kotlinSerializationJsonPresent) {
|
||||
writers.add(new EncoderHttpMessageWriter<>(getKotlinSerializationJsonEncoder()));
|
||||
}
|
||||
if (jackson2SmilePresent) {
|
||||
writers.add(new EncoderHttpMessageWriter<>(this.jackson2SmileEncoder != null ?
|
||||
(Jackson2SmileEncoder) this.jackson2SmileEncoder : new Jackson2SmileEncoder()));
|
||||
|
||||
+23
@@ -88,6 +88,28 @@ public class KotlinSerializationJsonHttpMessageConverter extends AbstractGeneric
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {
|
||||
try {
|
||||
serializer(GenericTypeResolver.resolveType(type, contextClass));
|
||||
return canRead(mediaType);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType) {
|
||||
try {
|
||||
serializer(type != null ? GenericTypeResolver.resolveType(type, clazz) : clazz);
|
||||
return canWrite(mediaType);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
|
||||
throws IOException, HttpMessageNotReadableException {
|
||||
@@ -151,6 +173,7 @@ public class KotlinSerializationJsonHttpMessageConverter extends AbstractGeneric
|
||||
* Tries to find a serializer that can marshall or unmarshall instances of the given type
|
||||
* using kotlinx.serialization. If no serializer can be found, an exception is thrown.
|
||||
* <p>Resolved serializers are cached and cached results are returned on successive calls.
|
||||
* TODO Avoid relying on throwing exception when https://github.com/Kotlin/kotlinx.serialization/pull/1164 is fixed
|
||||
* @param type the type to find a serializer for
|
||||
* @return a resolved serializer for the given type
|
||||
* @throws RuntimeException if no serializer supporting the given type can be found
|
||||
|
||||
@@ -232,8 +232,6 @@ final class DefaultPathContainer implements PathContainer {
|
||||
|
||||
private final String valueToMatch;
|
||||
|
||||
private final char[] valueToMatchAsChars;
|
||||
|
||||
private final MultiValueMap<String, String> parameters;
|
||||
|
||||
|
||||
@@ -243,7 +241,6 @@ final class DefaultPathContainer implements PathContainer {
|
||||
DefaultPathSegment(String value, String valueToMatch, MultiValueMap<String, String> params) {
|
||||
this.value = value;
|
||||
this.valueToMatch = valueToMatch;
|
||||
this.valueToMatchAsChars = valueToMatch.toCharArray();
|
||||
this.parameters = CollectionUtils.unmodifiableMultiValueMap(params);
|
||||
}
|
||||
|
||||
@@ -254,7 +251,6 @@ final class DefaultPathContainer implements PathContainer {
|
||||
this.value = value;
|
||||
this.valueToMatch = value.contains(separator.encodedSequence()) ?
|
||||
value.replaceAll(separator.encodedSequence(), separator.value()) : value;
|
||||
this.valueToMatchAsChars = this.valueToMatch.toCharArray();
|
||||
this.parameters = EMPTY_PARAMS;
|
||||
}
|
||||
|
||||
@@ -271,7 +267,7 @@ final class DefaultPathContainer implements PathContainer {
|
||||
|
||||
@Override
|
||||
public char[] valueToMatchAsChars() {
|
||||
return this.valueToMatchAsChars;
|
||||
return this.valueToMatch.toCharArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.http.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@code ServerHttpResponse} that delegates all calls to a
|
||||
* given target {@code ServerHttpResponse}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.3.2
|
||||
*/
|
||||
public class DelegatingServerHttpResponse implements ServerHttpResponse {
|
||||
|
||||
private final ServerHttpResponse delegate;
|
||||
|
||||
/**
|
||||
* Create a new {@code DelegatingServerHttpResponse}.
|
||||
* @param delegate the response to delegate to
|
||||
*/
|
||||
public DelegatingServerHttpResponse(ServerHttpResponse delegate) {
|
||||
Assert.notNull(delegate, "Delegate must not be null");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target response that this response delegates to.
|
||||
* @return the delegate
|
||||
*/
|
||||
public ServerHttpResponse getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatusCode(HttpStatus status) {
|
||||
this.delegate.setStatusCode(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
this.delegate.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getBody() throws IOException {
|
||||
return this.delegate.getBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return this.delegate.getHeaders();
|
||||
}
|
||||
|
||||
}
|
||||
+14
-5
@@ -20,6 +20,7 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -152,12 +153,14 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
|
||||
@Override
|
||||
@Nullable
|
||||
public String getFirst(String headerName) {
|
||||
String value = servletResponse.getHeader(headerName);
|
||||
if (value != null) {
|
||||
return value;
|
||||
if (headerName.equalsIgnoreCase(CONTENT_TYPE)) {
|
||||
// Content-Type is written as an override so check super first
|
||||
String value = super.getFirst(headerName);
|
||||
return (value != null ? value : servletResponse.getHeader(headerName));
|
||||
}
|
||||
else {
|
||||
return super.getFirst(headerName);
|
||||
String value = servletResponse.getHeader(headerName);
|
||||
return (value != null ? value : super.getFirst(headerName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +168,13 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
|
||||
public List<String> get(Object key) {
|
||||
Assert.isInstanceOf(String.class, key, "Key must be a String-based header name");
|
||||
|
||||
Collection<String> values1 = servletResponse.getHeaders((String) key);
|
||||
String headerName = (String) key;
|
||||
if (headerName.equalsIgnoreCase(CONTENT_TYPE)) {
|
||||
// Content-Type is written as an override so don't merge
|
||||
return Collections.singletonList(getFirst(headerName));
|
||||
}
|
||||
|
||||
Collection<String> values1 = servletResponse.getHeaders(headerName);
|
||||
if (headersWritten) {
|
||||
return new ArrayList<>(values1);
|
||||
}
|
||||
|
||||
+14
-1
@@ -69,7 +69,8 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest {
|
||||
* Constructor with the URI and headers for the request.
|
||||
* @param uri the URI for the request
|
||||
* @param contextPath the context path for the request
|
||||
* @param headers the headers for the request
|
||||
* @param headers the headers for the request (as {@link MultiValueMap})
|
||||
* @since 5.3
|
||||
*/
|
||||
public AbstractServerHttpRequest(URI uri, @Nullable String contextPath, MultiValueMap<String, String> headers) {
|
||||
this.uri = uri;
|
||||
@@ -77,6 +78,18 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest {
|
||||
this.headers = HttpHeaders.readOnlyHttpHeaders(headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with the URI and headers for the request.
|
||||
* @param uri the URI for the request
|
||||
* @param contextPath the context path for the request
|
||||
* @param headers the headers for the request (as {@link HttpHeaders})
|
||||
*/
|
||||
public AbstractServerHttpRequest(URI uri, @Nullable String contextPath, HttpHeaders headers) {
|
||||
this.uri = uri;
|
||||
this.path = RequestPath.parse(uri, contextPath);
|
||||
this.headers = HttpHeaders.readOnlyHttpHeaders(headers);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
|
||||
+32
-4
@@ -211,10 +211,29 @@ public abstract class AbstractServerHttpResponse implements ServerHttpResponse {
|
||||
// We must resolve value first however, for a chance to handle potential error.
|
||||
if (body instanceof Mono) {
|
||||
return ((Mono<? extends DataBuffer>) body)
|
||||
.flatMap(buffer -> doCommit(() ->
|
||||
writeWithInternal(Mono.fromCallable(() -> buffer)
|
||||
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release))))
|
||||
.doOnError(t -> getHeaders().clearContentHeaders());
|
||||
.flatMap(buffer -> {
|
||||
touchDataBuffer(buffer);
|
||||
AtomicReference<Boolean> subscribed = new AtomicReference<>(false);
|
||||
return doCommit(
|
||||
() -> {
|
||||
try {
|
||||
return writeWithInternal(Mono.fromCallable(() -> buffer)
|
||||
.doOnSubscribe(s -> subscribed.set(true))
|
||||
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release));
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
return Mono.error(ex);
|
||||
}
|
||||
})
|
||||
.doOnError(ex -> DataBufferUtils.release(buffer))
|
||||
.doOnCancel(() -> {
|
||||
if (!subscribed.get()) {
|
||||
DataBufferUtils.release(buffer);
|
||||
}
|
||||
});
|
||||
})
|
||||
.doOnError(t -> getHeaders().clearContentHeaders())
|
||||
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
|
||||
}
|
||||
else {
|
||||
return new ChannelSendOperator<>(body, inner -> doCommit(() -> writeWithInternal(inner)))
|
||||
@@ -316,4 +335,13 @@ public abstract class AbstractServerHttpResponse implements ServerHttpResponse {
|
||||
*/
|
||||
protected abstract void applyCookies();
|
||||
|
||||
/**
|
||||
* Allow sub-classes to associate a hint with the data buffer if it is a
|
||||
* pooled buffer and supports leak tracking.
|
||||
* @param buffer the buffer to attach a hint to
|
||||
* @since 5.3.2
|
||||
*/
|
||||
protected void touchDataBuffer(DataBuffer buffer) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+17
@@ -20,6 +20,9 @@ import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelId;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -27,6 +30,7 @@ import reactor.netty.http.server.HttpServerResponse;
|
||||
|
||||
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.NettyDataBufferFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -43,6 +47,9 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
class ReactorServerHttpResponse extends AbstractServerHttpResponse implements ZeroCopyHttpOutputMessage {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ReactorServerHttpResponse.class);
|
||||
|
||||
|
||||
private final HttpServerResponse response;
|
||||
|
||||
|
||||
@@ -115,4 +122,14 @@ class ReactorServerHttpResponse extends AbstractServerHttpResponse implements Ze
|
||||
Flux.from(dataBuffers).map(NettyDataBufferFactory::toByteBuf);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void touchDataBuffer(DataBuffer buffer) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
this.response.withConnection(connection -> {
|
||||
ChannelId id = connection.channel().id();
|
||||
DataBufferUtils.touch(buffer, "Channel id: " + id.asShortText());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ public class ContentNegotiationManagerFactoryBean
|
||||
|
||||
private String parameterName = "format";
|
||||
|
||||
private boolean favorPathExtension = true;
|
||||
private boolean favorPathExtension = false;
|
||||
|
||||
private Map<String, MediaType> mediaTypes = new HashMap<>();
|
||||
|
||||
|
||||
@@ -114,6 +114,8 @@ public @interface CrossOrigin {
|
||||
* {@code Expires}, {@code Last-Modified}, or {@code Pragma},
|
||||
* <p>Exposed headers are listed in the {@code Access-Control-Expose-Headers}
|
||||
* response header of actual CORS requests.
|
||||
* <p>The special value {@code "*"} allows all headers to be exposed for
|
||||
* non-credentialed requests.
|
||||
* <p>By default no headers are listed as exposed.
|
||||
*/
|
||||
String[] exposedHeaders() default {};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user