diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 5cdaa4ba7117..36717b4858e5 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,5 +1,5 @@ **Affects:** \ @@ -14,4 +14,4 @@ Thanks for taking the time to create an issue. Please read the following: Issue or Pull Request? Create only one, not both. GitHub treats them as the same. If unsure, start with an issue, and if you submit a pull request later, the issue will be closed as superseded. ---> \ No newline at end of file +--> diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml new file mode 100644 index 000000000000..405a2b306592 --- /dev/null +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -0,0 +1,10 @@ +name: "Validate Gradle Wrapper" +on: [push, pull_request] + +jobs: + validation: + name: "Validation" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: gradle/wrapper-validation-action@v1 diff --git a/.gitignore b/.gitignore index 3f904904f76f..b08c4536d776 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ out test-output atlassian-ide-plugin.xml .gradletasknamecache + +# VS Code +.vscode/ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7b0031d87d61..a236224cb58d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,8 +7,8 @@ First off, thank you for taking the time to contribute! :+1: :tada: * [Code of Conduct](#code-of-conduct) * [How to Contribute](#how-to-contribute) * [Discuss](#discuss) - * [Create a Ticket](#create-a-ticket) - * [Ticket Lifecycle](#ticket-lifecycle) + * [Create an Issue](#create-an-issue) + * [Issue Lifecycle](#issue-lifecycle) * [Submit a Pull Request](#submit-a-pull-request) * [Build from Source](#build-from-source) * [Source Code Style](#source-code-style) @@ -25,58 +25,58 @@ Please report unacceptable behavior to spring-code-of-conduct@pivotal.io. #### Discuss If you have a question, check Stack Overflow using -[this list of tags](https://spring.io/questions), organized by Spring project. +[this list of tags](https://stackoverflow.com/questions/tagged/spring+or+spring-mvc+or+spring-aop+or+spring-jdbc+or+spring-transactions+or+spring-annotations+or+spring-jms+or+spring-el+or+spring-test+or+spring+or+spring-remoting+or+spring-orm+or+spring-jmx+or+spring-cache+or+spring-webflux?tab=Newest). Find an existing discussion, or start a new one if necessary. -If you suspect an issue, perform a search in the -[GitHub issue tracker](https://github.com/spring-projects/spring-framework/issues), using a few different keywords. -When you find related issues and discussions, prior or current, it helps you to learn, and -it helps us to make a decision. +If you believe there is an issue, search through +[existing issues](https://github.com/spring-projects/spring-framework/issues) trying a +few different ways to find discussions, past or current, that are related to the issue. +Reading those discussions helps you to learn about the issue, and helps us to make a +decision. -#### Create a Ticket -Reporting an issue or making a feature request is a great way to contribute. Your feedback -and the conversations that result from it provide a continuous flow of ideas. +#### Create an Issue -Before you create a ticket, please take the time to [research first](#discuss). +Reporting an issue or making a feature request is a great way to contribute. Your feedback +and the conversations that result from it provide a continuous flow of ideas. However, +before creating a ticket, please take the time to [discuss and research](#discuss) first. -If creating a ticket after a discussion on Stack Overflow, please provide a self-sufficient description in the ticket, independent of the details on Stack Overflow. We understand this is extra work, but the issue tracker is an important place of record for design discussions and decisions that can often be referenced long after the fix version — for example to revisit decisions, to understand the origin of a feature, and so on. +If creating an issue after a discussion on Stack Overflow, please provide a description +in the issue instead of simply referring to Stack Overflow. The issue tracker is an +important place of record for design discussions and should be self-sufficient. -Once you're ready, create a ticket in the [GitHub issue tracker](https://github.com/spring-projects/spring-framework/issues). +Once you're ready, create an issue on +[GitHub](https://github.com/spring-projects/spring-framework/issues). -#### Ticket Lifecycle +#### Issue Lifecycle When an issue is first created, it is flagged `waiting-for-triage` waiting for a team -member to triage it. Within a day or two, the issue will then be reviewed, and the team -may ask for further information if needed. Based on the findings, the issue is either -assigned a fix version or declined. +member to triage it. Once the issue has been reviewed, the team may ask for further +information if needed, and based on the findings, the issue is either assigned a target +milestone or is closed with a specific status. -When a fix is ready, the issue is closed and may still be re-opened. Once a fix is -released, the issue can't be reopened. If necessary, you will need to create a new, -related ticket with a fresh description. +When a fix is ready, the issue is closed and may still be re-opened until the fix is +released. After that the issue will typically no longer be reopened. In rare cases if the +issue was not at all fixed, the issue may be re-opened. In most cases however any +follow-up reports will need to be created as new issues with a fresh description. #### Submit a Pull Request -You can contribute a source code change by submitting a pull request. - 1. If you have not previously done so, please sign the -[Contributor License Agreement](https://cla.pivotal.io/sign/spring). You will also be reminded -automatically when you submit a pull request. +[Contributor License Agreement](https://cla.pivotal.io/sign/spring). You will be reminded +automatically when you submit the PR. -1. Should you create a ticket first? The answer is no. Just create the pull request and use -the description to provide context and motivation, as you would for an issue. If you want -to start a discussion first or have already created an issue, once a pull request is created, -we will close the issue as superseded by the pull request, and the discussion of the issue -will continue under the pull request. +1. Should you create an issue first? No, just create the pull request and use the +description to provide context and motivation, as you would for an issue. If you want +to start a discussion first or have already created an issue, once a pull request is +created, we will close the issue as superseded by the pull request, and the discussion +about the issue will continue under the pull request. 1. Always check out the `master` branch and submit pull requests against it (for target version see [settings.gradle](settings.gradle)). Backports to prior versions will be considered on a case-by-case basis and reflected as the fix version in the issue tracker. -1. Use short branch names, preferably based on the GitHub issue (e.g. `22276`), or -otherwise using succinct, lower-case, dash (-) delimited names, such as `fix-warnings`. - 1. Choose the granularity of your commits consciously and squash commits that represent multiple edits or corrections of the same logical change. See [Rewriting History section of Pro Git](https://git-scm.com/book/en/Git-Tools-Rewriting-History) @@ -87,7 +87,8 @@ for the description, followed by the issue fixed, e.g. `Closes gh-22276`. See th [Commit Guidelines section of Pro Git](https://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines) for best practices around commit messages, and use `git log` to see some examples. -1. List the GitHub issue number in the PR description. +1. If there is a prior issue, reference the GitHub issue number in the description of the +pull request. If accepted, your contribution may be heavily modified as needed prior to merging. You will likely retain author attribution for your Git commits granted that the bulk of @@ -124,10 +125,7 @@ The reference documentation is in the [src/docs/asciidoc](src/docs/asciidoc) dir edit source files, and submit directly from GitHub. When making changes locally, execute `./gradlew asciidoctor` and then browse the result under -`build/asciidoc/html5/index.html`. +`build/docs/ref-docs/html5/index.html`. -Asciidoctor also supports live editing. For more details read -[Editing AsciiDoc with Live Preview](https://asciidoctor.org/docs/editing-asciidoc-with-live-preview/). -Note that if you choose the -[System Monitor](https://asciidoctor.org/docs/editing-asciidoc-with-live-preview/#using-a-system-monitor) -option, you can find a Guardfile under `src/docs/asciidoc`. +Asciidoctor also supports live editing. For more details see +[AsciiDoc Tooling](https://docs.asciidoctor.org/asciidoctor/latest/tooling/). diff --git a/README.md b/README.md index 32aa262d7325..d3d16d18d118 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Spring Framework [![Build Status](https://build.spring.io/plugins/servlet/wittified/build-status/SPR-PUBM)](https://build.spring.io/browse/SPR) +# Spring Framework [![Build Status](https://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-5.3.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x?groups=Build") [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring) This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring". @@ -17,10 +17,18 @@ For access to artifacts or a distribution zip, see the [Spring Framework Artifac The Spring Framework maintains reference documentation ([published](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/) and [source](src/docs/asciidoc)), Github [wiki pages](https://github.com/spring-projects/spring-framework/wiki), and an [API reference](https://docs.spring.io/spring-framework/docs/current/javadoc-api/). There are also [guides and tutorials](https://spring.io/guides) across Spring projects. +## Micro-Benchmarks + +See the [Micro-Benchmarks](https://github.com/spring-projects/spring-framework/wiki/Micro-Benchmarks) Wiki page. + ## Build from Source See the [Build from Source](https://github.com/spring-projects/spring-framework/wiki/Build-from-Source) Wiki page and the [CONTRIBUTING.md](CONTRIBUTING.md) file. +## Continuous Integration Builds + +Information regarding CI builds can be found in the [Spring Framework Concourse pipeline](ci/README.adoc) documentation. + ## Stay in Touch Follow [@SpringCentral](https://twitter.com/springcentral), [@SpringFramework](https://twitter.com/springframework), and its [team members](https://twitter.com/springframework/lists/team/members) on Twitter. In-depth articles can be found at [The Spring Blog](https://spring.io/blog/), and releases are announced via our [news feed](https://spring.io/blog/category/news). diff --git a/build.gradle b/build.gradle index 3b28c31172c1..88324bc313b4 100644 --- a/build.gradle +++ b/build.gradle @@ -1,33 +1,16 @@ -buildscript { - dependencies { - classpath 'org.asciidoctor:asciidoctorj-pdf:1.5.0-alpha.16' - classpath 'io.spring.asciidoctor:spring-asciidoctor-extensions:0.1.3.RELEASE' - } -} - plugins { - id 'io.spring.dependency-management' version '1.0.8.RELEASE' apply false - id 'org.jetbrains.kotlin.jvm' version '1.3.50' apply false - id 'org.jetbrains.dokka' version '0.9.18' apply false - id 'org.asciidoctor.convert' version '1.5.8' - id 'io.spring.nohttp' version '0.0.3.RELEASE' - id 'de.undercouch.download' version '4.0.0' - id 'com.gradle.build-scan' version '2.4.2' - id "com.jfrog.artifactory" version '4.9.8' apply false - id "io.freefair.aspectj" version "4.1.1" apply false - id "com.github.ben-manes.versions" version "0.24.0" -} - -if (System.getenv('GRADLE_ENTERPRISE_URL')) { - apply from: "$rootDir/gradle/build-scan-user-data.gradle" - buildScan { - captureTaskInputFiles = true - obfuscation { - ipAddresses { addresses -> addresses.collect { address -> '0.0.0.0'} } - } - publishAlways() - server = System.getenv('GRADLE_ENTERPRISE_URL') - } + 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.32' 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' + id 'de.undercouch.download' version '4.1.1' + id "io.freefair.aspectj" version '5.1.1' apply false + id "com.github.ben-manes.versions" version '0.28.0' + id "com.github.johnrengelman.shadow" version "6.1.0" apply false + id "me.champeau.gradle.jmh" version "0.5.2" apply false + id "org.jetbrains.kotlin.plugin.serialization" version "1.4.32" apply false } ext { @@ -43,74 +26,83 @@ configure(allprojects) { project -> dependencyManagement { imports { - mavenBom "com.fasterxml.jackson:jackson-bom:2.10.0" - mavenBom "io.netty:netty-bom:4.1.43.Final" - mavenBom "io.projectreactor:reactor-bom:Dysprosium-SR1" - mavenBom "io.rsocket:rsocket-bom:1.0.0-RC5" - mavenBom "org.eclipse.jetty:jetty-bom:9.4.21.v20190926" - mavenBom "org.jetbrains.kotlin:kotlin-bom:1.3.50" - mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.3.2" - mavenBom "org.junit:junit-bom:5.5.2" + mavenBom "com.fasterxml.jackson:jackson-bom:2.12.2" + mavenBom "io.netty:netty-bom:4.1.63.Final" + mavenBom "io.projectreactor:reactor-bom:2020.0.6" + mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR9" + mavenBom "io.rsocket:rsocket-bom:1.1.0" + mavenBom "org.eclipse.jetty:jetty-bom:9.4.39.v20210325" + mavenBom "org.jetbrains.kotlin:kotlin-bom:1.4.32" + mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.4.3" + mavenBom "org.junit:junit-bom:5.7.1" } dependencies { - dependencySet(group: 'org.apache.logging.log4j', version: '2.12.1') { + dependencySet(group: 'org.apache.logging.log4j', version: '2.14.1') { entry 'log4j-api' entry 'log4j-core' - entry 'log4j-slf4j-impl' entry 'log4j-jul' + entry 'log4j-slf4j-impl' + } + dependency "org.slf4j:slf4j-api:1.7.30" + dependency("com.google.code.findbugs:findbugs:3.0.1") { + exclude group: "dom4j", name: "dom4j" } - dependency "org.slf4j:slf4j-api:1.7.28" - dependency "com.google.code.findbugs:jsr305:3.0.2" - dependencySet(group: 'org.aspectj', version: '1.9.4') { + dependencySet(group: 'org.aspectj', version: '1.9.6') { entry 'aspectjrt' entry 'aspectjtools' entry 'aspectjweaver' } - dependencySet(group: 'org.codehaus.groovy', version: '2.5.8') { + dependencySet(group: 'org.codehaus.groovy', version: '3.0.7') { entry 'groovy' entry 'groovy-jsr223' - entry 'groovy-templates' + entry 'groovy-templates' // requires findbugs for warning-free compilation entry 'groovy-test' entry 'groovy-xml' } dependency "io.reactivex:rxjava:1.3.8" dependency "io.reactivex:rxjava-reactive-streams:1.2.1" - dependency "io.reactivex.rxjava2:rxjava:2.2.13" + dependency "io.reactivex.rxjava2:rxjava:2.2.21" + dependency "io.reactivex.rxjava3:rxjava:3.0.12" + dependency "io.projectreactor.tools:blockhound:1.0.4.RELEASE" - dependency "com.caucho:hessian:4.0.62" + dependency "com.caucho:hessian:4.0.63" dependency "com.fasterxml:aalto-xml:1.2.2" - dependency("com.fasterxml.woodstox:woodstox-core:5.2.0") { + dependency("com.fasterxml.woodstox:woodstox-core:6.2.4") { exclude group: "stax", name: "stax-api" } dependency "com.google.code.gson:gson:2.8.6" - dependency "com.google.protobuf:protobuf-java-util:3.10.0" + dependency "com.google.protobuf:protobuf-java-util:3.15.5" dependency "com.googlecode.protobuf-java-format:protobuf-java-format:1.4" - dependency("com.thoughtworks.xstream:xstream:1.4.11.1") { + dependency("com.thoughtworks.xstream:xstream:1.4.15") { exclude group: "xpp3", name: "xpp3_min" exclude group: "xmlpull", name: "xmlpull" } - dependency "org.apache.johnzon:johnzon-jsonb:1.2.1" + dependency "org.apache.johnzon:johnzon-jsonb:1.2.10" dependency("org.codehaus.jettison:jettison:1.3.8") { exclude group: "stax", name: "stax-api" } - dependencySet(group: 'org.jibx', version: '1.3.1') { + dependencySet(group: 'org.jibx', version: '1.3.3') { entry 'jibx-bind' entry 'jibx-run' } dependency "org.ogce:xpp3:1.1.6" - dependency "org.yaml:snakeyaml:1.25" + dependency "org.yaml:snakeyaml:1.28" + dependencySet(group: 'org.jetbrains.kotlinx', version: '1.0.1') { + entry 'kotlinx-serialization-core' + entry 'kotlinx-serialization-json' + } dependency "com.h2database:h2:1.4.200" - dependency "com.github.ben-manes.caffeine:caffeine:2.8.0" - dependency "com.github.librepdf:openpdf:1.3.11" - dependency "com.rometools:rome:1.12.2" + dependency "com.github.ben-manes.caffeine:caffeine:2.9.0" + dependency "com.github.librepdf:openpdf:1.3.25" + dependency "com.rometools:rome:1.15.0" dependency "commons-io:commons-io:2.5" - dependency "io.vavr:vavr:0.10.0" + dependency "io.vavr:vavr:0.10.3" dependency "net.sf.jopt-simple:jopt-simple:5.0.4" - dependencySet(group: 'org.apache.activemq', version: '5.8.0') { + dependencySet(group: 'org.apache.activemq', version: '5.16.1') { entry 'activemq-broker' entry('activemq-kahadb-store') { exclude group: "org.springframework", name: "spring-context" @@ -118,37 +110,37 @@ configure(allprojects) { project -> entry 'activemq-stomp' } dependency "org.apache.bcel:bcel:6.0" - dependency "org.apache.commons:commons-pool2:2.6.0" + dependency "org.apache.commons:commons-pool2:2.9.0" dependencySet(group: 'org.apache.derby', version: '10.14.2.0') { entry 'derby' entry 'derbyclient' } - dependency "org.apache.poi:poi-ooxml:4.1.1" - dependency "org.beanshell:bsh:2.0b5" - dependency "org.freemarker:freemarker:2.3.29" - dependency "org.hsqldb:hsqldb:2.5.0" + dependency "org.apache.poi:poi-ooxml:4.1.2" + dependency "org.apache-extras.beanshell:bsh:2.0b6" + dependency "org.freemarker:freemarker:2.3.31" + dependency "org.hsqldb:hsqldb:2.5.1" dependency "org.quartz-scheduler:quartz:2.3.2" dependency "org.codehaus.fabric3.api:commonj:1.1.0" 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.8.Final" - dependency "org.hibernate:hibernate-validator:6.0.17.Final" - dependency "org.webjars:webjars-locator-core:0.42" + dependency "org.hibernate:hibernate-core:5.4.30.Final" + dependency "org.hibernate:hibernate-validator:6.2.0.Final" + dependency "org.webjars:webjars-locator-core:0.46" dependency "org.webjars:underscorejs:1.8.3" - dependencySet(group: 'org.apache.tomcat', version: '9.0.27') { + dependencySet(group: 'org.apache.tomcat', version: '9.0.45') { 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.27') { + dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.45') { entry 'tomcat-embed-core' entry 'tomcat-embed-websocket' } - dependencySet(group: 'io.undertow', version: '2.0.27.Final') { + dependencySet(group: 'io.undertow', version: '2.2.7.Final') { entry 'undertow-core' entry('undertow-websockets-jsr') { exclude group: "org.jboss.spec.javax.websocket", name: "jboss-websocket-api_1.1_spec" @@ -159,62 +151,65 @@ configure(allprojects) { project -> } } - dependencySet(group: 'com.squareup.okhttp3', version: '3.14.3') { + dependencySet(group: 'com.squareup.okhttp3', version: '3.14.9') { entry 'okhttp' entry 'mockwebserver' } - dependency("org.apache.httpcomponents:httpclient:4.5.10") { + 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.eclipse.jetty:jetty-reactive-httpclient:1.0.3" + dependency 'org.apache.httpcomponents.client5:httpclient5:5.0.3' + dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.0.3' + dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.6" - dependency "org.jruby:jruby:9.2.7.0" + dependency "org.jruby:jruby:9.2.16.0" dependency "org.python:jython-standalone:2.7.1" - dependency "org.mozilla:rhino:1.7.10" + dependency "org.mozilla:rhino:1.7.11" dependency "commons-fileupload:commons-fileupload:1.4" dependency "org.synchronoss.cloud:nio-multipart-parser:1.1.0" - dependency("dom4j:dom4j:1.6.1") { - exclude group: "xml-apis", name: "xml-apis" + dependency("org.dom4j:dom4j:2.1.3") { + exclude group: "jaxen", name: "jaxen" + exclude group: "net.java.dev.msv", name: "xsdlib" + exclude group: "pull-parser", name: "pull-parser" + exclude group: "xpp3", name: "xpp3" } - dependency("jaxen:jaxen:1.1.1") { - exclude group: "xml-apis", name: "xml-apis" - exclude group: "xom", name: "xom" - exclude group: "xerces", name: "xercesImpl" + dependency("jaxen:jaxen:1.2.0") { + exclude group: "dom4j", name: "dom4j" } - dependency("junit:junit:4.12") { + dependency("junit:junit:4.13.2") { exclude group: "org.hamcrest", name: "hamcrest-core" } dependency("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.1") { exclude group: "junit", name: "junit" } - dependency "org.testng:testng:6.14.3" + dependency "org.testng:testng:7.4.0" dependency "org.hamcrest:hamcrest:2.1" - dependency "org.awaitility:awaitility:3.1.3" - dependency "org.assertj:assertj-core:3.14.0" - dependencySet(group: 'org.xmlunit', version: '2.6.2') { + dependency "org.awaitility:awaitility:3.1.6" + dependency "org.assertj:assertj-core:3.19.0" + dependencySet(group: 'org.xmlunit', version: '2.8.2') { entry 'xmlunit-assertj' entry('xmlunit-matchers') { exclude group: "org.hamcrest", name: "hamcrest-core" } } - dependencySet(group: 'org.mockito', version: '3.1.0') { + dependencySet(group: 'org.mockito', version: '3.8.0') { entry('mockito-core') { exclude group: "org.hamcrest", name: "hamcrest-core" } entry 'mockito-junit-jupiter' } - dependency "io.mockk:mockk:1.9.3" + dependency "io.mockk:mockk:1.10.2" - dependency("net.sourceforge.htmlunit:htmlunit:2.36.0") { + dependency("net.sourceforge.htmlunit:htmlunit:2.48.0") { exclude group: "commons-logging", name: "commons-logging" } - dependency("org.seleniumhq.selenium:htmlunit-driver:2.36.0") { + dependency("org.seleniumhq.selenium:htmlunit-driver:2.48.0") { exclude group: "commons-logging", name: "commons-logging" } dependency("org.seleniumhq.selenium:selenium-java:3.141.59") { @@ -222,7 +217,8 @@ configure(allprojects) { project -> exclude group: "io.netty", name: "netty" } dependency "org.skyscreamer:jsonassert:1.5.0" - dependency "com.jayway.jsonpath:json-path:2.4.0" + dependency "com.jayway.jsonpath:json-path:2.5.0" + dependency "org.bouncycastle:bcpkix-jdk15on:1.66" dependencySet(group: 'org.apache.tiles', version: '3.0.8') { entry 'tiles-api' @@ -240,9 +236,9 @@ configure(allprojects) { project -> } dependency "com.ibm.websphere:uow:6.0.2.17" - dependency "com.jamonapi:jamon:2.81" - dependency "joda-time:joda-time:2.10.4" - dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.5" + dependency "com.jamonapi:jamon:2.82" + dependency "joda-time:joda-time:2.10.10" + dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.8" dependency "org.javamoney:moneta:1.3" dependency "com.sun.activation:javax.activation:1.2.0" @@ -310,22 +306,24 @@ configure([rootProject] + javaProjects) { project -> group = "org.springframework" apply plugin: "java" + apply plugin: "java-test-fixtures" apply plugin: "checkstyle" apply plugin: 'org.springframework.build.compile' + apply from: "${rootDir}/gradle/toolchains.gradle" apply from: "${rootDir}/gradle/ide.gradle" pluginManager.withPlugin("kotlin") { apply plugin: "org.jetbrains.dokka" compileKotlin { kotlinOptions { - jvmTarget = "1.8" + languageVersion = "1.3" + apiVersion = "1.3" freeCompilerArgs = ["-Xjsr305=strict"] allWarningsAsErrors = true } } compileTestKotlin { kotlinOptions { - jvmTarget = "1.8" freeCompilerArgs = ["-Xjsr305=strict"] } } @@ -340,8 +338,8 @@ configure([rootProject] + javaProjects) { project -> } checkstyle { - toolVersion = "8.26" - configDir = rootProject.file("src/checkstyle") + toolVersion = "8.41" + configDirectory.set(rootProject.file("src/checkstyle")) } dependencies { @@ -374,14 +372,15 @@ configure([rootProject] + javaProjects) { project -> "https://tiles.apache.org/tiles-request/apidocs/", "https://tiles.apache.org/framework/apidocs/", "https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/", - "https://www.ehcache.org/apidocs/2.10.4", + "https://www.ehcache.org/apidocs/2.10.4/", "https://www.quartz-scheduler.org/api/2.3.0/", "https://fasterxml.github.io/jackson-core/javadoc/2.10/", "https://fasterxml.github.io/jackson-databind/javadoc/2.10/", "https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.10/", "https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/", - "https://junit.org/junit4/javadoc/4.12/", - "https://junit.org/junit5/docs/5.5.2/api/" + "https://projectreactor.io/docs/test/release/api/", + "https://junit.org/junit4/javadoc/4.13.2/", + "https://junit.org/junit5/docs/5.7.1/api/" ] as String[] } @@ -401,7 +400,7 @@ configure(rootProject) { nohttp { source.exclude "**/test-output/**" - whitelistFile = project.file("src/nohttp/whitelist.lines") + allowlistFile = project.file("src/nohttp/allowlist.lines") def rootPath = file(rootDir).toPath() def projectDirs = allprojects.collect { it.projectDir } + "${rootDir}/buildSrc" projectDirs.forEach { dir -> @@ -414,10 +413,6 @@ configure(rootProject) { } } - dependencies { - asciidoctor("io.spring.asciidoctor:spring-asciidoctor-extensions:0.1.3.RELEASE") - } - publishing { publications { mavenJava(MavenPublication) { diff --git a/buildSrc/README.md b/buildSrc/README.md index 7c722a362730..ada387cf7144 100644 --- a/buildSrc/README.md +++ b/buildSrc/README.md @@ -1,4 +1,4 @@ -# Spring Framework build +# Spring Framework Build This folder contains the custom plugins and conventions for the Spring Framework build. They are declared in the `build.gradle` file in this folder. @@ -7,17 +7,11 @@ They are declared in the `build.gradle` file in this folder. ### Compiler conventions -The `org.springframework.build.compile` applies the Java compiler conventions to the build. -By default, the build is compiling sources with the `1.8` source and target compatibility. -You can test a different source compatibility version on the CLI with a project property like: - -``` -./gradlew test -PjavaSourceVersion=11 -``` +The `org.springframework.build.compile` plugin applies the Java compiler conventions to the build. ## Build Plugins -## Optional dependencies +### Optional dependencies The `org.springframework.build.optional-dependencies` plugin creates a new `optional` Gradle configuration - it adds the dependencies to the project's compile and runtime classpath @@ -25,17 +19,11 @@ but doesn't affect the classpath of dependent projects. This plugin does not provide a `provided` configuration, as the native `compileOnly` and `testCompileOnly` configurations are preferred. -## Test sources - -The `org.springframework.build.test-sources` updates `testCompile` dependencies to include -the test source sets of `project()` dependencies. This plugin is used in the Spring Framework build -to share test utilities and fixtures amongst modules. - -## API Diff +### API Diff This plugin uses the [Gradle JApiCmp](https://github.com/melix/japicmp-gradle-plugin) plugin to generate API Diff reports for each Spring Framework module. This plugin is applied once on the root -project and create tasks in each framework module. Unlike previous versions of this part of the build, +project and creates tasks in each framework module. Unlike previous versions of this part of the build, there is no need for checking out a specific tag. The plugin will fetch the JARs we want to compare the current working version with. You can generate the reports for all modules or a single module: @@ -44,4 +32,4 @@ current working version with. You can generate the reports for all modules or a ./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE ``` -The reports are located under `build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/`. \ No newline at end of file +The reports are located under `build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/`. diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 1aed5f6e78a2..03f629496a28 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -9,7 +9,7 @@ repositories { dependencies { implementation "me.champeau.gradle:japicmp-gradle-plugin:0.2.8" - implementation "com.google.guava:guava:18.0" // required by japicmp-gradle-plugin + implementation "com.google.guava:guava:28.2-jre" // required by japicmp-gradle-plugin } gradlePlugin { @@ -26,9 +26,5 @@ gradlePlugin { id = "org.springframework.build.optional-dependencies" implementationClass = "org.springframework.build.optional.OptionalDependenciesPlugin" } - testSourcesPlugin { - id = "org.springframework.build.test-sources" - implementationClass = "org.springframework.build.testsources.TestSourcesPlugin" - } } } diff --git a/buildSrc/settings.gradle b/buildSrc/settings.gradle index ec93f39d06cc..e69de29bb2d1 100644 --- a/buildSrc/settings.gradle +++ b/buildSrc/settings.gradle @@ -1 +0,0 @@ -apply from: "../gradle/build-cache-settings.gradle" diff --git a/buildSrc/src/main/java/org/springframework/build/compile/CompilerConventionsPlugin.java b/buildSrc/src/main/java/org/springframework/build/compile/CompilerConventionsPlugin.java index 5cb9a70a37e5..5284df28f124 100644 --- a/buildSrc/src/main/java/org/springframework/build/compile/CompilerConventionsPlugin.java +++ b/buildSrc/src/main/java/org/springframework/build/compile/CompilerConventionsPlugin.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,33 +20,24 @@ import java.util.Arrays; import java.util.List; -import org.gradle.api.JavaVersion; import org.gradle.api.Plugin; import org.gradle.api.Project; +import org.gradle.api.plugins.JavaLibraryPlugin; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.compile.JavaCompile; /** * {@link Plugin} that applies conventions for compiling Java sources in Spring Framework. - *

One can override the default Java source compatibility version - * with a dedicated property on the CLI: {@code "./gradlew test -PjavaSourceVersion=11"}. * * @author Brian Clozel + * @author Sam Brannen */ public class CompilerConventionsPlugin implements Plugin { - /** - * The project property that can be used to switch the Java source - * compatibility version for building source and test classes. - */ - public static String JAVA_SOURCE_VERSION_PROPERTY = "javaSourceVersion"; - - public static JavaVersion DEFAULT_COMPILER_VERSION = JavaVersion.VERSION_1_8; + private static final List COMPILER_ARGS; - private static List COMPILER_ARGS; - - private static List TEST_COMPILER_ARGS; + private static final List TEST_COMPILER_ARGS; static { List commonCompilerArgs = Arrays.asList( @@ -68,35 +59,29 @@ public class CompilerConventionsPlugin implements Plugin { @Override public void apply(Project project) { - project.getPlugins().withType(JavaPlugin.class, javaPlugin -> applyJavaCompileConventions(project)); + project.getPlugins().withType(JavaLibraryPlugin.class, javaPlugin -> applyJavaCompileConventions(project)); } /** - * Applies the common Java compiler options for sources and test sources. + * Applies the common Java compiler options for main sources, test fixture sources, and + * test sources. * @param project the current project */ private void applyJavaCompileConventions(Project project) { JavaPluginConvention java = project.getConvention().getPlugin(JavaPluginConvention.class); - if (project.hasProperty(JAVA_SOURCE_VERSION_PROPERTY)) { - JavaVersion javaSourceVersion = JavaVersion.toVersion(project.property(JAVA_SOURCE_VERSION_PROPERTY)); - java.setSourceCompatibility(javaSourceVersion); - } - else { - java.setSourceCompatibility(DEFAULT_COMPILER_VERSION); - } - java.setTargetCompatibility(DEFAULT_COMPILER_VERSION); - project.getTasks().withType(JavaCompile.class) - .matching(javaCompile -> javaCompile.getName().equals(JavaPlugin.COMPILE_JAVA_TASK_NAME)) + .matching(compileTask -> compileTask.getName().equals(JavaPlugin.COMPILE_JAVA_TASK_NAME)) .forEach(compileTask -> { compileTask.getOptions().setCompilerArgs(COMPILER_ARGS); compileTask.getOptions().setEncoding("UTF-8"); }); project.getTasks().withType(JavaCompile.class) - .matching(javaCompile -> javaCompile.getName().equals(JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME)) + .matching(compileTask -> compileTask.getName().equals(JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME) + || compileTask.getName().equals("compileTestFixturesJava")) .forEach(compileTask -> { compileTask.getOptions().setCompilerArgs(TEST_COMPILER_ARGS); compileTask.getOptions().setEncoding("UTF-8"); }); } -} \ No newline at end of file + +} diff --git a/buildSrc/src/main/java/org/springframework/build/testsources/TestSourcesPlugin.java b/buildSrc/src/main/java/org/springframework/build/testsources/TestSourcesPlugin.java deleted file mode 100644 index 68b97661902c..000000000000 --- a/buildSrc/src/main/java/org/springframework/build/testsources/TestSourcesPlugin.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2002-2019 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.build.testsources; - -import java.util.Arrays; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.ProjectDependency; -import org.gradle.api.plugins.JavaPlugin; -import org.gradle.api.plugins.JavaPluginConvention; -import org.gradle.api.tasks.SourceSet; -import org.gradle.api.tasks.SourceSetOutput; - -import org.springframework.build.optional.OptionalDependenciesPlugin; - -/** - * {@link Plugin} that automatically updates testCompile dependencies to include - * the test source sets of {@code project()} dependencies. - * - *

This plugin is used in the Spring Framework build to share test utilities and fixtures - * between projects. - * - * @author Phillip Webb - * @author Brian Clozel - */ -public class TestSourcesPlugin implements Plugin { - - /** - * List of configurations this plugin should look for project dependencies in. - */ - @SuppressWarnings("deprecation") - private static final List CONFIGURATIONS = Arrays.asList( - JavaPlugin.COMPILE_CONFIGURATION_NAME, - JavaPlugin.API_CONFIGURATION_NAME, - JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, - OptionalDependenciesPlugin.OPTIONAL_CONFIGURATION_NAME, - JavaPlugin.TEST_COMPILE_CONFIGURATION_NAME); - - @Override - public void apply(Project project) { - project.getPlugins().withType(JavaPlugin.class, (plugin) -> addTestSourcesToProject(project)); - } - - private void addTestSourcesToProject(Project project) { - project.afterEvaluate(currentProject -> { - Set projectDependencies = new LinkedHashSet<>(); - collectProjectDependencies(projectDependencies, project); - projectDependencies.forEach(dep -> addTestSourcesFromDependency(currentProject, dep)); - }); - } - - private void collectProjectDependencies(Set projectDependencies, Project project) { - for (String configurationName : CONFIGURATIONS) { - Configuration configuration = project.getConfigurations().findByName(configurationName); - if (configuration != null) { - configuration.getDependencies().forEach(dependency -> { - if (dependency instanceof ProjectDependency) { - ProjectDependency projectDependency = (ProjectDependency) dependency; - projectDependencies.add(projectDependency); - collectProjectDependencies(projectDependencies, projectDependency.getDependencyProject()); - } - }); - } - } - } - - private void addTestSourcesFromDependency(final Project currentProject, ProjectDependency dependency) { - Project dependencyProject = dependency.getDependencyProject(); - dependencyProject.getPlugins().withType(JavaPlugin.class, plugin -> { - final JavaPluginConvention javaPlugin = dependencyProject.getConvention() - .getPlugin(JavaPluginConvention.class); - SourceSetOutput test = javaPlugin.getSourceSets().findByName(SourceSet.TEST_SOURCE_SET_NAME).getOutput(); - currentProject.getDependencies().add(JavaPlugin.TEST_COMPILE_CONFIGURATION_NAME, test); - }); - } -} diff --git a/ci/README.adoc b/ci/README.adoc new file mode 100644 index 000000000000..cb617637d9b4 --- /dev/null +++ b/ci/README.adoc @@ -0,0 +1,57 @@ +== Spring Framework Concourse pipeline + +The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks. +The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline +for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x[Spring Framework 5.3.x]. + +=== Setting up your development environment + +If you're part of the Spring Framework project on GitHub, you can get access to CI management features. +First, you need to go to https://ci.spring.io and install the client CLI for your platform (see bottom right of the screen). + +You can then login with the instance using: + +[source] +---- +$ fly -t spring login -n spring-framework -c https://ci.spring.io +---- + +Once logged in, you should get something like: + +[source] +---- +$ fly ts +name url team expiry +spring https://ci.spring.io spring-framework Wed, 25 Mar 2020 17:45:26 UTC +---- + +=== Pipeline configuration and structure + +The build pipelines are described in `pipeline.yml` file. + +This file is listing Concourse resources, i.e. build inputs and outputs such as container images, artifact repositories, source repositories, notification services, etc. + +It also describes jobs (a job is a sequence of inputs, tasks and outputs); jobs are organized by groups. + +The `pipeline.yml` definition contains `((parameters))` which are loaded from the `parameters.yml` file or from our https://docs.cloudfoundry.org/credhub/[credhub instance]. + +You'll find in this folder the following resources: + +* `pipeline.yml` the build pipeline +* `parameters.yml` the build parameters used for the pipeline +* `images/` holds the container images definitions used in this pipeline +* `scripts/` holds the build scripts that ship within the CI container images +* `tasks` contains the task definitions used in the main `pipeline.yml` + +=== Updating the build pipeline + +Updating files on the repository is not enough to update the build pipeline, as changes need to be applied. + +The pipeline can be deployed using the following command: + +[source] +---- +$ fly -t spring set-pipeline -p spring-framework-5.3.x -c ci/pipeline.yml -l ci/parameters.yml +---- + +NOTE: This assumes that you have credhub integration configured with the appropriate secrets. diff --git a/ci/config/changelog-generator.yml b/ci/config/changelog-generator.yml new file mode 100644 index 000000000000..248e58db739e --- /dev/null +++ b/ci/config/changelog-generator.yml @@ -0,0 +1,17 @@ +changelog: + repository: spring-projects/spring-framework + sections: + - title: ":star: New Features" + labels: + - "type: enhancement" + - title: ":beetle: Bug Fixes" + labels: + - "type: bug" + - "type: regression" + - title: ":notebook_with_decorative_cover: Documentation" + labels: + - "type: documentation" + - title: ":hammer: Dependency Upgrades" + sort: "title" + labels: + - "type: dependency-upgrade" diff --git a/ci/config/release-scripts.yml b/ci/config/release-scripts.yml new file mode 100644 index 000000000000..d31f8cba00dc --- /dev/null +++ b/ci/config/release-scripts.yml @@ -0,0 +1,10 @@ +logging: + level: + io.spring.concourse: DEBUG +spring: + main: + banner-mode: off +sonatype: + exclude: + - 'build-info\.json' + - '.*\.zip' diff --git a/ci/images/README.adoc b/ci/images/README.adoc new file mode 100644 index 000000000000..6da9addd9ca5 --- /dev/null +++ b/ci/images/README.adoc @@ -0,0 +1,21 @@ +== CI Images + +These images are used by CI to run the actual builds. + +To build the image locally run the following from this directory: + +---- +$ docker build --no-cache -f /Dockerfile . +---- + +For example + +---- +$ docker build --no-cache -f spring-framework-ci-image/Dockerfile . +---- + +To test run: + +---- +$ docker run -it --entrypoint /bin/bash +---- diff --git a/ci/images/ci-image/Dockerfile b/ci/images/ci-image/Dockerfile new file mode 100644 index 000000000000..1ce41f37a932 --- /dev/null +++ b/ci/images/ci-image/Dockerfile @@ -0,0 +1,11 @@ +FROM ubuntu:focal-20210119 + +ADD setup.sh /setup.sh +ADD get-jdk-url.sh /get-jdk-url.sh +RUN ./setup.sh java8 + +ENV JAVA_HOME /opt/openjdk/java8 +ENV JDK11 /opt/openjdk/java11 +ENV JDK15 /opt/openjdk/java15 + +ENV PATH $JAVA_HOME/bin:$PATH diff --git a/ci/images/get-jdk-url.sh b/ci/images/get-jdk-url.sh new file mode 100755 index 000000000000..f4f3ab84cf40 --- /dev/null +++ b/ci/images/get-jdk-url.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +case "$1" in + java8) + echo "https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u282-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u282b08.tar.gz" + ;; + java11) + echo "https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.10%2B9/OpenJDK11U-jdk_x64_linux_hotspot_11.0.10_9.tar.gz" + ;; + java15) + echo "https://github.com/AdoptOpenJDK/openjdk15-binaries/releases/download/jdk-15.0.2%2B7/OpenJDK15U-jdk_x64_linux_hotspot_15.0.2_7.tar.gz" + ;; + *) + echo $"Unknown java version" + exit 1 +esac diff --git a/ci/images/setup.sh b/ci/images/setup.sh new file mode 100755 index 000000000000..b226ab7de75c --- /dev/null +++ b/ci/images/setup.sh @@ -0,0 +1,42 @@ +#!/bin/bash +set -ex + +########################################################### +# UTILS +########################################################### + +export DEBIAN_FRONTEND=noninteractive +apt-get update +apt-get install --no-install-recommends -y tzdata ca-certificates net-tools libxml2-utils git curl libudev1 libxml2-utils iptables iproute2 jq fontconfig +ln -fs /usr/share/zoneinfo/UTC /etc/localtime +dpkg-reconfigure --frontend noninteractive tzdata +rm -rf /var/lib/apt/lists/* + +curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/concourse-java.sh > /opt/concourse-java.sh + +curl --output /opt/concourse-release-scripts.jar https://repo.spring.io/release/io/spring/concourse/releasescripts/concourse-release-scripts/0.3.2/concourse-release-scripts-0.3.2.jar + +########################################################### +# JAVA +########################################################### + +mkdir -p /opt/openjdk +pushd /opt/openjdk > /dev/null +for jdk in java8 java11 java15 +do + JDK_URL=$( /get-jdk-url.sh $jdk ) + mkdir $jdk + pushd $jdk > /dev/null + curl -L ${JDK_URL} | tar zx --strip-components=1 + test -f bin/java + test -f bin/javac + popd > /dev/null +done +popd + +########################################################### +# GRADLE ENTERPRISE +########################################################### +cd / +mkdir ~/.gradle +echo 'systemProp.user.name=concourse' > ~/.gradle/gradle.properties diff --git a/ci/parameters.yml b/ci/parameters.yml new file mode 100644 index 000000000000..578a1b892998 --- /dev/null +++ b/ci/parameters.yml @@ -0,0 +1,13 @@ +email-server: "smtp.svc.pivotal.io" +email-from: "ci@spring.io" +email-to: ["spring-framework-dev@pivotal.io"] +github-repo: "https://github.com/spring-projects/spring-framework.git" +github-repo-name: "spring-projects/spring-framework" +docker-hub-organization: "springci" +artifactory-server: "https://repo.spring.io" +branch: "master" +milestone: "5.3.x" +build-name: "spring-framework" +pipeline-name: "spring-framework" +concourse-url: "https://ci.spring.io" +task-timeout: 1h00m diff --git a/ci/pipeline.yml b/ci/pipeline.yml new file mode 100644 index 000000000000..e77e3b3990ae --- /dev/null +++ b/ci/pipeline.yml @@ -0,0 +1,414 @@ +anchors: + git-repo-resource-source: &git-repo-resource-source + uri: ((github-repo)) + username: ((github-username)) + password: ((github-password)) + branch: ((branch)) + gradle-enterprise-task-params: &gradle-enterprise-task-params + GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key)) + GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username)) + GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password)) + sonatype-task-params: &sonatype-task-params + SONATYPE_USERNAME: ((sonatype-username)) + SONATYPE_PASSWORD: ((sonatype-password)) + SONATYPE_URL: ((sonatype-url)) + SONATYPE_STAGING_PROFILE_ID: ((sonatype-staging-profile-id)) + artifactory-task-params: &artifactory-task-params + ARTIFACTORY_SERVER: ((artifactory-server)) + ARTIFACTORY_USERNAME: ((artifactory-username)) + ARTIFACTORY_PASSWORD: ((artifactory-password)) + build-project-task-params: &build-project-task-params + privileged: true + timeout: ((task-timeout)) + params: + BRANCH: ((branch)) + <<: *gradle-enterprise-task-params + docker-resource-source: &docker-resource-source + username: ((docker-hub-username)) + password: ((docker-hub-password)) + tag: ((milestone)) + slack-fail-params: &slack-fail-params + text: > + :concourse-failed: + [$TEXT_FILE_CONTENT] + text_file: git-repo/build/build-scan-uri.txt + silent: true + icon_emoji: ":concourse:" + username: concourse-ci + changelog-task-params: &changelog-task-params + name: generated-changelog/tag + tag: generated-changelog/tag + body: generated-changelog/changelog.md + github-task-params: &github-task-params + GITHUB_USERNAME: ((github-username)) + GITHUB_TOKEN: ((github-ci-release-token)) + +resource_types: +- name: artifactory-resource + type: registry-image + source: + repository: springio/artifactory-resource + tag: 0.0.13 +- name: github-status-resource + type: registry-image + source: + repository: dpb587/github-status-resource + tag: master +- name: slack-notification + type: registry-image + source: + repository: cfcommunity/slack-notification-resource + tag: latest +resources: +- name: git-repo + type: git + icon: github + source: + <<: *git-repo-resource-source +- name: every-morning + type: time + icon: alarm + source: + start: 8:00 AM + stop: 9:00 AM + location: Europe/Vienna +- name: ci-images-git-repo + type: git + icon: github + source: + uri: ((github-repo)) + branch: ((branch)) + paths: ["ci/images/*"] +- name: ci-image + type: docker-image + icon: docker + source: + <<: *docker-resource-source + repository: ((docker-hub-organization))/spring-framework-ci +- name: artifactory-repo + type: artifactory-resource + icon: package-variant + source: + uri: ((artifactory-server)) + username: ((artifactory-username)) + password: ((artifactory-password)) + build_name: ((build-name)) +- name: repo-status-build + type: github-status-resource + icon: eye-check-outline + source: + repository: ((github-repo-name)) + access_token: ((github-ci-status-token)) + branch: ((branch)) + context: build +- name: repo-status-jdk11-build + type: github-status-resource + icon: eye-check-outline + source: + repository: ((github-repo-name)) + access_token: ((github-ci-status-token)) + branch: ((branch)) + context: jdk11-build +- name: repo-status-jdk15-build + type: github-status-resource + icon: eye-check-outline + source: + repository: ((github-repo-name)) + access_token: ((github-ci-status-token)) + branch: ((branch)) + context: jdk15-build +- name: slack-alert + type: slack-notification + icon: slack + source: + url: ((slack-webhook-url)) +- name: github-pre-release + type: github-release + icon: briefcase-download-outline + source: + owner: spring-projects + repository: spring-framework + access_token: ((github-ci-release-token)) + pre_release: true + release: false +- name: github-release + type: github-release + icon: briefcase-download + source: + owner: spring-projects + repository: spring-framework + access_token: ((github-ci-release-token)) + pre_release: false +jobs: +- name: build-ci-images + plan: + - get: ci-images-git-repo + trigger: true + - in_parallel: + - put: ci-image + params: + build: ci-images-git-repo/ci/images + dockerfile: ci-images-git-repo/ci/images/ci-image/Dockerfile +- name: build + serial: true + public: true + plan: + - get: ci-image + - get: git-repo + trigger: true + - put: repo-status-build + params: { state: "pending", commit: "git-repo" } + - do: + - task: build-project + image: ci-image + file: git-repo/ci/tasks/build-project.yml + <<: *build-project-task-params + on_failure: + do: + - put: repo-status-build + params: { state: "failure", commit: "git-repo" } + - put: slack-alert + params: + <<: *slack-fail-params + - put: repo-status-build + params: { state: "success", commit: "git-repo" } + - put: artifactory-repo + params: &artifactory-params + signing_key: ((signing-key)) + signing_passphrase: ((signing-passphrase)) + repo: libs-snapshot-local + folder: distribution-repository + build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}" + build_number: "${BUILD_PIPELINE_NAME}-${BUILD_JOB_NAME}-${BUILD_NAME}" + disable_checksum_uploads: true + threads: 8 + artifact_set: + - include: + - "/**/spring-*.zip" + properties: + "zip.name": "spring-framework" + "zip.displayname": "Spring Framework" + "zip.deployed": "false" + - include: + - "/**/spring-*-docs.zip" + properties: + "zip.type": "docs" + - include: + - "/**/spring-*-dist.zip" + properties: + "zip.type": "dist" + - include: + - "/**/spring-*-schema.zip" + properties: + "zip.type": "schema" + get_params: + threads: 8 +- name: jdk11-build + serial: true + public: true + plan: + - get: ci-image + - get: git-repo + - get: every-morning + trigger: true + - put: repo-status-jdk11-build + params: { state: "pending", commit: "git-repo" } + - do: + - task: check-project + image: ci-image + file: git-repo/ci/tasks/check-project.yml + params: + MAIN_TOOLCHAIN: 8 + TEST_TOOLCHAIN: 11 + <<: *build-project-task-params + on_failure: + do: + - put: repo-status-jdk11-build + params: { state: "failure", commit: "git-repo" } + - put: slack-alert + params: + <<: *slack-fail-params + - put: repo-status-jdk11-build + params: { state: "success", commit: "git-repo" } +- name: jdk15-build + serial: true + public: true + plan: + - get: ci-image + - get: git-repo + - get: every-morning + trigger: true + - put: repo-status-jdk15-build + params: { state: "pending", commit: "git-repo" } + - do: + - task: check-project + image: ci-image + file: git-repo/ci/tasks/check-project.yml + params: + MAIN_TOOLCHAIN: 8 + TEST_TOOLCHAIN: 15 + <<: *build-project-task-params + on_failure: + do: + - put: repo-status-jdk15-build + params: { state: "failure", commit: "git-repo" } + - put: slack-alert + params: + <<: *slack-fail-params + - put: repo-status-jdk15-build + params: { state: "success", commit: "git-repo" } +- name: stage-milestone + serial: true + plan: + - get: ci-image + - get: git-repo + trigger: false + - task: stage + image: ci-image + file: git-repo/ci/tasks/stage-version.yml + params: + RELEASE_TYPE: M + <<: *gradle-enterprise-task-params + - put: artifactory-repo + params: + <<: *artifactory-params + repo: libs-staging-local + - put: git-repo + params: + repository: stage-git-repo +- name: promote-milestone + serial: true + plan: + - get: ci-image + - get: git-repo + trigger: false + - get: artifactory-repo + trigger: false + passed: [stage-milestone] + params: + download_artifacts: false + save_build_info: true + - task: promote + image: ci-image + file: git-repo/ci/tasks/promote-version.yml + params: + RELEASE_TYPE: M + <<: *artifactory-task-params + - task: generate-changelog + file: git-repo/ci/tasks/generate-changelog.yml + params: + RELEASE_TYPE: M + <<: *github-task-params + - put: github-pre-release + params: + <<: *changelog-task-params +- name: stage-rc + serial: true + plan: + - get: ci-image + - get: git-repo + trigger: false + - task: stage + image: ci-image + file: git-repo/ci/tasks/stage-version.yml + params: + RELEASE_TYPE: RC + <<: *gradle-enterprise-task-params + - put: artifactory-repo + params: + <<: *artifactory-params + repo: libs-staging-local + - put: git-repo + params: + repository: stage-git-repo +- name: promote-rc + serial: true + plan: + - get: ci-image + - get: git-repo + trigger: false + - get: artifactory-repo + trigger: false + passed: [stage-rc] + params: + download_artifacts: false + save_build_info: true + - task: promote + image: ci-image + file: git-repo/ci/tasks/promote-version.yml + params: + RELEASE_TYPE: RC + <<: *artifactory-task-params + - task: generate-changelog + file: git-repo/ci/tasks/generate-changelog.yml + params: + RELEASE_TYPE: RC + <<: *github-task-params + - put: github-pre-release + params: + <<: *changelog-task-params +- name: stage-release + serial: true + plan: + - get: ci-image + - get: git-repo + trigger: false + - task: stage + image: ci-image + file: git-repo/ci/tasks/stage-version.yml + params: + RELEASE_TYPE: RELEASE + <<: *gradle-enterprise-task-params + - put: artifactory-repo + params: + <<: *artifactory-params + repo: libs-staging-local + - put: git-repo + params: + repository: stage-git-repo +- name: promote-release + serial: true + plan: + - get: ci-image + - get: git-repo + trigger: false + - get: artifactory-repo + trigger: false + passed: [stage-release] + params: + download_artifacts: true + save_build_info: true + - task: promote + image: ci-image + file: git-repo/ci/tasks/promote-version.yml + params: + RELEASE_TYPE: RELEASE + <<: *artifactory-task-params + <<: *sonatype-task-params +- name: create-github-release + serial: true + plan: + - get: ci-image + - get: git-repo + - get: artifactory-repo + trigger: true + passed: [promote-release] + params: + download_artifacts: false + save_build_info: true + - task: generate-changelog + file: git-repo/ci/tasks/generate-changelog.yml + params: + RELEASE_TYPE: RELEASE + <<: *github-task-params + - put: github-release + params: + <<: *changelog-task-params + +groups: +- name: "builds" + jobs: ["build", "jdk11-build", "jdk15-build"] +- name: "releases" + jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"] +- name: "ci-images" + jobs: ["build-ci-images"] diff --git a/ci/scripts/build-project.sh b/ci/scripts/build-project.sh new file mode 100755 index 000000000000..3844d1a3ddb4 --- /dev/null +++ b/ci/scripts/build-project.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +source $(dirname $0)/common.sh +repository=$(pwd)/distribution-repository + +pushd git-repo > /dev/null +./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository +popd > /dev/null diff --git a/ci/scripts/check-project.sh b/ci/scripts/check-project.sh new file mode 100755 index 000000000000..f2bf454e3597 --- /dev/null +++ b/ci/scripts/check-project.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +source $(dirname $0)/common.sh + +pushd git-repo > /dev/null +./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Dorg.gradle.java.installations.fromEnv=JDK11,JDK15 \ + -PmainToolchain=$MAIN_TOOLCHAIN -PtestToolchain=$TEST_TOOLCHAIN --no-daemon --max-workers=4 check +popd > /dev/null diff --git a/ci/scripts/common.sh b/ci/scripts/common.sh new file mode 100644 index 000000000000..1accaa616732 --- /dev/null +++ b/ci/scripts/common.sh @@ -0,0 +1,2 @@ +source /opt/concourse-java.sh +setup_symlinks \ No newline at end of file diff --git a/ci/scripts/generate-changelog.sh b/ci/scripts/generate-changelog.sh new file mode 100755 index 000000000000..d3d2b97e5dba --- /dev/null +++ b/ci/scripts/generate-changelog.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e + +CONFIG_DIR=git-repo/ci/config +version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' ) + +java -jar /github-changelog-generator.jar \ + --spring.config.location=${CONFIG_DIR}/changelog-generator.yml \ + ${version} generated-changelog/changelog.md + +echo ${version} > generated-changelog/version +echo v${version} > generated-changelog/tag diff --git a/ci/scripts/promote-version.sh b/ci/scripts/promote-version.sh new file mode 100755 index 000000000000..44c5ff626f91 --- /dev/null +++ b/ci/scripts/promote-version.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +source $(dirname $0)/common.sh +CONFIG_DIR=git-repo/ci/config + +version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' ) +export BUILD_INFO_LOCATION=$(pwd)/artifactory-repo/build-info.json + +java -jar /opt/concourse-release-scripts.jar \ + --spring.config.location=${CONFIG_DIR}/release-scripts.yml \ + publishToCentral $RELEASE_TYPE $BUILD_INFO_LOCATION artifactory-repo || { exit 1; } + +java -jar /opt/concourse-release-scripts.jar \ + --spring.config.location=${CONFIG_DIR}/release-scripts.yml \ + promote $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; } + +echo "Promotion complete" +echo $version > version/version diff --git a/ci/scripts/stage-version.sh b/ci/scripts/stage-version.sh new file mode 100755 index 000000000000..0b285de014bf --- /dev/null +++ b/ci/scripts/stage-version.sh @@ -0,0 +1,50 @@ +#!/bin/bash +set -e + +source $(dirname $0)/common.sh +repository=$(pwd)/distribution-repository + +pushd git-repo > /dev/null +git fetch --tags --all > /dev/null +popd > /dev/null + +git clone git-repo stage-git-repo > /dev/null + +pushd stage-git-repo > /dev/null + +snapshotVersion=$( awk -F '=' '$1 == "version" { print $2 }' gradle.properties ) +if [[ $RELEASE_TYPE = "M" ]]; then + stageVersion=$( get_next_milestone_release $snapshotVersion) + nextVersion=$snapshotVersion +elif [[ $RELEASE_TYPE = "RC" ]]; then + stageVersion=$( get_next_rc_release $snapshotVersion) + nextVersion=$snapshotVersion +elif [[ $RELEASE_TYPE = "RELEASE" ]]; then + stageVersion=$( get_next_release $snapshotVersion) + nextVersion=$( bump_version_number $snapshotVersion) +else + echo "Unknown release type $RELEASE_TYPE" >&2; exit 1; +fi + +echo "Staging $stageVersion (next version will be $nextVersion)" +sed -i "s/version=$snapshotVersion/version=$stageVersion/" gradle.properties + +git config user.name "Spring Buildmaster" > /dev/null +git config user.email "buildmaster@springframework.org" > /dev/null +git add gradle.properties > /dev/null +git commit -m"Release v$stageVersion" > /dev/null +git tag -a "v$stageVersion" -m"Release v$stageVersion" > /dev/null + +./gradlew --no-daemon --max-workers=4 -PdeploymentRepository=${repository} build publishAllPublicationsToDeploymentRepository + +git reset --hard HEAD^ > /dev/null +if [[ $nextVersion != $snapshotVersion ]]; then + echo "Setting next development version (v$nextVersion)" + sed -i "s/version=$snapshotVersion/version=$nextVersion/" gradle.properties + git add gradle.properties > /dev/null + git commit -m"Next development version (v$nextVersion)" > /dev/null +fi; + +echo "Staging Complete" + +popd > /dev/null diff --git a/ci/scripts/sync-to-maven-central.sh b/ci/scripts/sync-to-maven-central.sh new file mode 100755 index 000000000000..b42631164ed5 --- /dev/null +++ b/ci/scripts/sync-to-maven-central.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +export BUILD_INFO_LOCATION=$(pwd)/artifactory-repo/build-info.json +version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' ) +java -jar /opt/concourse-release-scripts.jar syncToCentral "RELEASE" $BUILD_INFO_LOCATION || { exit 1; } + +echo "Sync complete" +echo $version > version/version diff --git a/ci/tasks/build-project.yml b/ci/tasks/build-project.yml new file mode 100644 index 000000000000..759749ef433f --- /dev/null +++ b/ci/tasks/build-project.yml @@ -0,0 +1,22 @@ +--- +platform: linux +inputs: +- name: git-repo +outputs: +- name: distribution-repository +- name: git-repo +caches: +- path: gradle +params: + BRANCH: + CI: true + GRADLE_ENTERPRISE_ACCESS_KEY: + GRADLE_ENTERPRISE_CACHE_USERNAME: + GRADLE_ENTERPRISE_CACHE_PASSWORD: + GRADLE_ENTERPRISE_URL: https://ge.spring.io +run: + path: bash + args: + - -ec + - | + ${PWD}/git-repo/ci/scripts/build-project.sh diff --git a/ci/tasks/check-project.yml b/ci/tasks/check-project.yml new file mode 100644 index 000000000000..bea1185231b9 --- /dev/null +++ b/ci/tasks/check-project.yml @@ -0,0 +1,24 @@ +--- +platform: linux +inputs: +- name: git-repo +outputs: +- name: distribution-repository +- name: git-repo +caches: +- path: gradle +params: + BRANCH: + CI: true + MAIN_TOOLCHAIN: + TEST_TOOLCHAIN: + GRADLE_ENTERPRISE_ACCESS_KEY: + GRADLE_ENTERPRISE_CACHE_USERNAME: + GRADLE_ENTERPRISE_CACHE_PASSWORD: + GRADLE_ENTERPRISE_URL: https://ge.spring.io +run: + path: bash + args: + - -ec + - | + ${PWD}/git-repo/ci/scripts/check-project.sh diff --git a/ci/tasks/generate-changelog.yml b/ci/tasks/generate-changelog.yml new file mode 100755 index 000000000000..ea048af96a0f --- /dev/null +++ b/ci/tasks/generate-changelog.yml @@ -0,0 +1,20 @@ +--- +platform: linux +image_resource: + type: registry-image + source: + repository: springio/github-changelog-generator + tag: '0.0.6' +inputs: +- name: git-repo +- name: artifactory-repo +outputs: +- name: generated-changelog +params: + GITHUB_ORGANIZATION: + GITHUB_REPO: + GITHUB_USERNAME: + GITHUB_TOKEN: + RELEASE_TYPE: +run: + path: git-repo/ci/scripts/generate-changelog.sh diff --git a/ci/tasks/promote-version.yml b/ci/tasks/promote-version.yml new file mode 100644 index 000000000000..abdd8fed5c5c --- /dev/null +++ b/ci/tasks/promote-version.yml @@ -0,0 +1,18 @@ +--- +platform: linux +inputs: +- name: git-repo +- name: artifactory-repo +outputs: +- name: version +params: + RELEASE_TYPE: + ARTIFACTORY_SERVER: + ARTIFACTORY_USERNAME: + ARTIFACTORY_PASSWORD: + SONATYPE_USER: + SONATYPE_PASSWORD: + SONATYPE_URL: + SONATYPE_STAGING_PROFILE_ID: +run: + path: git-repo/ci/scripts/promote-version.sh diff --git a/ci/tasks/stage-version.yml b/ci/tasks/stage-version.yml new file mode 100644 index 000000000000..ded11483a76b --- /dev/null +++ b/ci/tasks/stage-version.yml @@ -0,0 +1,17 @@ +--- +platform: linux +inputs: +- name: git-repo +outputs: +- name: stage-git-repo +- name: distribution-repository +params: + RELEASE_TYPE: + CI: true + GRADLE_ENTERPRISE_CACHE_USERNAME: + GRADLE_ENTERPRISE_CACHE_PASSWORD: + GRADLE_ENTERPRISE_URL: https://ge.spring.io +caches: +- path: gradle +run: + path: git-repo/ci/scripts/stage-version.sh diff --git a/framework-bom/framework-bom.gradle b/framework-bom/framework-bom.gradle index bf9d620a807f..840f20537fa2 100644 --- a/framework-bom/framework-bom.gradle +++ b/framework-bom/framework-bom.gradle @@ -18,12 +18,6 @@ publishing { mavenJava(MavenPublication) { artifactId = 'spring-framework-bom' from components.javaPlatform - // remove scope information from published BOM - pom.withXml { - asNode().dependencyManagement.first().dependencies.first().each { - it.remove(it.scope.first()) - } - } } } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index c7a95ea35442..ea82e6f78ea1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,5 @@ -version=5.2.2.BUILD-SNAPSHOT +version=5.3.7-SNAPSHOT org.gradle.jvmargs=-Xmx1536M org.gradle.caching=true org.gradle.parallel=true +kotlin.stdlib.default.dependency=false \ No newline at end of file diff --git a/gradle/build-cache-settings.gradle b/gradle/build-cache-settings.gradle deleted file mode 100644 index d62ea57ebd26..000000000000 --- a/gradle/build-cache-settings.gradle +++ /dev/null @@ -1,20 +0,0 @@ -buildCache { - local { - enabled = true - } - if (System.getenv('GRADLE_ENTERPRISE_URL')) { - remote(HttpBuildCache) { - enabled = true - url = "${System.getenv('GRADLE_ENTERPRISE_URL')}/cache/" - def cacheUsername = System.getenv('GRADLE_ENTERPRISE_CACHE_USERNAME') - def cachePassword = System.getenv('GRADLE_ENTERPRISE_CACHE_PASSWORD') - if (cacheUsername && cachePassword) { - push = true - credentials { - username = cacheUsername - password = cachePassword - } - } - } - } -} diff --git a/gradle/build-scan-user-data.gradle b/gradle/build-scan-user-data.gradle deleted file mode 100644 index d4f483d66f26..000000000000 --- a/gradle/build-scan-user-data.gradle +++ /dev/null @@ -1,90 +0,0 @@ -tagOs() -tagIde() -tagCiOrLocal() -addCiMetadata() -addGitMetadata() -addTestTaskMetadata() - -void tagOs() { - buildScan.tag System.getProperty('os.name') -} - -void tagIde() { - if (System.getProperty('idea.version')) { - buildScan.tag 'IntelliJ IDEA' - } else if (System.getProperty('eclipse.buildId')) { - buildScan.tag 'Eclipse' - } -} - -void tagCiOrLocal() { - buildScan.tag(isCi() ? 'CI' : 'LOCAL') -} - -void addGitMetadata() { - buildScan.background { - def gitCommitId = execAndGetStdout('git', 'rev-parse', '--short=8', '--verify', 'HEAD') - def gitBranchName = execAndGetStdout('git', 'rev-parse', '--abbrev-ref', 'HEAD') - def gitStatus = execAndGetStdout('git', 'status', '--porcelain') - - if(gitCommitId) { - def commitIdLabel = 'Git Commit ID' - value commitIdLabel, gitCommitId - link 'Git commit build scans', customValueSearchUrl([(commitIdLabel): gitCommitId]) - } - if (gitBranchName) { - tag gitBranchName - value 'Git branch', gitBranchName - } - if (gitStatus) { - tag 'dirty' - value 'Git status', gitStatus - } - } -} - -void addCiMetadata() { - def ciBuild = 'CI BUILD' - if (isBamboo()) { - buildScan.link ciBuild, System.getenv('bamboo_resultsUrl') - } -} - -void addTestTaskMetadata() { - allprojects { - tasks.withType(Test) { test -> - doFirst { - buildScan.value "Test#maxParallelForks[${test.path}]", test.maxParallelForks.toString() - } - } - } -} - -boolean isCi() { - isBamboo() -} - -boolean isBamboo() { - System.getenv('bamboo_resultsUrl') -} - -String execAndGetStdout(String... args) { - def stdout = new ByteArrayOutputStream() - exec { - commandLine(args) - standardOutput = stdout - } - return stdout.toString().trim() -} - -String customValueSearchUrl(Map search) { - def query = search.collect { name, value -> - "search.names=${encodeURL(name)}&search.values=${encodeURL(value)}" - }.join('&') - - "$buildScan.server/scans?$query" -} - -String encodeURL(String url) { - URLEncoder.encode(url, 'UTF-8') -} diff --git a/gradle/docs.gradle b/gradle/docs.gradle index 51b51252e860..4209b79fdc02 100644 --- a/gradle/docs.gradle +++ b/gradle/docs.gradle @@ -1,3 +1,20 @@ +configurations { + asciidoctorExt +} + +dependencies { + asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.5.0") +} + +repositories { + maven { + url "https://repo.spring.io/release" + mavenContent { + includeGroup "io.spring.asciidoctor" + } + } +} + /** * Produce Javadoc for all Spring Framework modules in "build/docs/javadoc" */ @@ -48,39 +65,55 @@ dokka { dependsOn { tasks.getByName("api") } + doFirst { - classpath = moduleProjects.collect { project -> project.jar.outputs.files.getFiles() }.flatten() - classpath += files(moduleProjects.collect { it.sourceSets.main.compileClasspath }) - sourceDirs = files(moduleProjects - .findAll { - it.pluginManager.hasPlugin("kotlin") + configuration { + classpath = moduleProjects.collect { project -> project.jar.outputs.files.getFiles() }.flatten() + classpath += files(moduleProjects.collect { it.sourceSets.main.compileClasspath }) + + moduleProjects.findAll { + it.pluginManager.hasPlugin("kotlin") + }.each { project -> + def kotlinDirs = project.sourceSets.main.kotlin.srcDirs.collect() + kotlinDirs -= project.sourceSets.main.java.srcDirs + kotlinDirs.each { dir -> + if (dir.exists()) { + sourceRoot { + path = dir.path + } + } } - .collect { project -> - def kotlinDirs = project.sourceSets.main.kotlin.srcDirs.collect() - kotlinDirs -= project.sourceSets.main.java.srcDirs - }) + } + } } - moduleName = "spring-framework" + outputFormat = "html" outputDirectory = "$buildDir/docs/kdoc" - externalDocumentationLink { - url = new URL("https://docs.spring.io/spring-framework/docs/$version/javadoc-api/") - packageListUrl = new File(buildDir, "docs/javadoc/package-list").toURI().toURL() - } - externalDocumentationLink { - url = new URL("https://projectreactor.io/docs/core/release/api/") - } - externalDocumentationLink { - url = new URL("https://www.reactive-streams.org/reactive-streams-1.0.1-javadoc/") - } - externalDocumentationLink { - url = new URL("https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/") + configuration { + moduleName = "spring-framework" + + externalDocumentationLink { + url = new URL("https://docs.spring.io/spring-framework/docs/$version/javadoc-api/") + packageListUrl = new File(buildDir, "docs/javadoc/package-list").toURI().toURL() + } + externalDocumentationLink { + url = new URL("https://projectreactor.io/docs/core/release/api/") + } + externalDocumentationLink { + url = new URL("https://www.reactive-streams.org/reactive-streams-1.0.1-javadoc/") + } + externalDocumentationLink { + url = new URL("https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/") + } + externalDocumentationLink { + url = new URL("https://r2dbc.io/spec/0.8.3.RELEASE/api/") + } } } task downloadResources(type: Download) { - def version = "0.1.3.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") @@ -93,27 +126,9 @@ task extractDocResources(type: Copy, dependsOn: downloadResources) { into "$buildDir/docs/spring-docs-resources/" } -/** - * Produce the Spring Framework Reference documentation - * from "src/docs/asciidoc" into "build/asciidoc/html5" - */ -asciidoctor { - sources { - include '*.adoc' - } - outputDir "$buildDir/docs/ref-docs/" - resources { - from(sourceDir) { - include 'images/*', 'css/**', 'js/**' - } - from "$buildDir/docs/spring-docs-resources/" - } - logDocuments = true - backends = ["html5"] - // only ouput PDF documentation for non-SNAPSHOT builds - if (!project.getVersion().toString().contains("BUILD-SNAPSHOT")) { - backends += "pdf" - } +asciidoctorj { + version = '2.4.1' + fatalWarnings ".*" options doctype: 'book', eruby: 'erubis' attributes([ icons: 'font', @@ -125,19 +140,51 @@ asciidoctor { sectnums: '', 'source-highlighter': 'highlight.js', highlightjsdir: 'js/highlight', - 'highlightjs-theme': 'github', + 'highlightjs-theme': 'googlecode', stylesdir: 'css/', stylesheet: 'stylesheet.css', 'spring-version': project.version ]) } -asciidoctor.dependsOn extractDocResources +/** + * Generate the Spring Framework Reference documentation from "src/docs/asciidoc" + * in "build/docs/ref-docs/html5". + */ +asciidoctor { + baseDirFollowsSourceDir() + configurations 'asciidoctorExt' + sources { + include '*.adoc' + } + outputDir "$buildDir/docs/ref-docs/html5" + logDocuments = true + resources { + from(sourceDir) { + include 'images/*.png', 'css/**', 'js/**' + } + from extractDocResources + } +} + +/** + * Generate the Spring Framework Reference documentation from "src/docs/asciidoc" + * in "build/docs/ref-docs/pdf". + */ +asciidoctorPdf { + baseDirFollowsSourceDir() + configurations 'asciidoctorExt' + sources { + include '*.adoc' + } + outputDir "$buildDir/docs/ref-docs/pdf" + logDocuments = true +} /** * Zip all docs (API and reference) into a single archive */ -task docsZip(type: Zip, dependsOn: ['api', 'asciidoctor', 'dokka']) { +task docsZip(type: Zip, dependsOn: ['api', 'asciidoctor', 'asciidoctorPdf', 'dokka']) { group = "Distribution" description = "Builds -${archiveClassifier} archive containing api and reference " + "for deployment at https://docs.spring.io/spring-framework/docs." @@ -150,11 +197,11 @@ task docsZip(type: Zip, dependsOn: ['api', 'asciidoctor', 'dokka']) { from (api) { into "javadoc-api" } - from ("$asciidoctor.outputDir/html5") { - into "spring-framework-reference" + from ("$asciidoctor.outputDir") { + into "reference/html" } - from ("$asciidoctor.outputDir/pdf") { - into "spring-framework-reference/pdf" + from ("$asciidoctorPdf.outputDir") { + into "reference/pdf" } from (dokka) { into "kdoc-api" @@ -175,14 +222,14 @@ task schemaZip(type: Zip) { def Properties schemas = new Properties(); module.sourceSets.main.resources.find { - it.path.endsWith("META-INF/spring.schemas") + (it.path.endsWith("META-INF/spring.schemas") || it.path.endsWith("META-INF\\spring.schemas")) }?.withInputStream { schemas.load(it) } for (def key : schemas.keySet()) { def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1') assert shortName != key File xsdFile = module.sourceSets.main.resources.find { - it.path.endsWith(schemas.get(key)) + (it.path.endsWith(schemas.get(key)) || it.path.endsWith(schemas.get(key).replaceAll('\\/','\\\\'))) } assert xsdFile != null into (shortName) { diff --git a/gradle/ide.gradle b/gradle/ide.gradle index 554d4b3c5432..48dea87a9b31 100644 --- a/gradle/ide.gradle +++ b/gradle/ide.gradle @@ -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,15 @@ eclipse.classpath.file.whenMerged { classpath -> } } +// 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" diff --git a/gradle/publications.gradle b/gradle/publications.gradle index 97d6e51f05f6..86e0d2221c0b 100644 --- a/gradle/publications.gradle +++ b/gradle/publications.gradle @@ -1,5 +1,4 @@ apply plugin: "maven-publish" -apply plugin: 'com.jfrog.artifactory' publishing { publications { @@ -50,6 +49,16 @@ publishing { } } -artifactoryPublish { - publications(publishing.publications.mavenJava) +configureDeploymentRepository(project) + +void configureDeploymentRepository(Project project) { + project.plugins.withType(MavenPublishPlugin.class).all { + PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class); + if (project.hasProperty("deploymentRepository")) { + publishing.repositories.maven { + it.url = project.property("deploymentRepository") + it.name = "deployment" + } + } + } } \ No newline at end of file diff --git a/gradle/spring-module.gradle b/gradle/spring-module.gradle index c70263e18c0a..49efaeae84dc 100644 --- a/gradle/spring-module.gradle +++ b/gradle/spring-module.gradle @@ -1,8 +1,38 @@ +apply plugin: 'java-library' apply plugin: 'org.springframework.build.compile' apply plugin: 'org.springframework.build.optional-dependencies' -apply plugin: 'org.springframework.build.test-sources' +// Uncomment the following for Shadow support in the jmhJar block. +// Currently commented out due to ZipException: archive is not a ZIP archive +// apply plugin: 'com.github.johnrengelman.shadow' +apply plugin: 'me.champeau.gradle.jmh' apply from: "$rootDir/gradle/publications.gradle" +dependencies { + jmh 'org.openjdk.jmh:jmh-core:1.25' + jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.25' + jmh 'net.sf.jopt-simple:jopt-simple:4.6' +} + +jmh { + duplicateClassesStrategy = DuplicatesStrategy.EXCLUDE +} + +jmhJar { + // Uncomment the following for Shadow's Transformer support. + // mergeServiceFiles() + // append('META-INF/spring.handlers') + // append('META-INF/spring.schemas') + // append('META-INF/spring.tooling') + exclude 'LICENSE' + exclude 'THIRD-PARTY' + exclude 'META-INF/license.txt' + exclude 'META-INF/notice.txt' + exclude 'META-INF/DEPENDENCIES' + exclude 'META-INF/LICENSE*' + exclude 'META-INF/NOTICE' + exclude 'META-INF/THIRD-PARTY' +} + jar { manifest.attributes["Implementation-Title"] = project.name manifest.attributes["Implementation-Version"] = project.version @@ -62,3 +92,7 @@ publishing { } } } + +// Disable publication of test fixture artifacts. +components.java.withVariantsFromConfiguration(configurations.testFixturesApiElements) { skip() } +components.java.withVariantsFromConfiguration(configurations.testFixturesRuntimeElements) { skip() } diff --git a/gradle/toolchains.gradle b/gradle/toolchains.gradle new file mode 100644 index 000000000000..c6a61fe38414 --- /dev/null +++ b/gradle/toolchains.gradle @@ -0,0 +1,124 @@ +/** + * Apply the JVM Toolchain conventions + * See https://docs.gradle.org/current/userguide/toolchains.html + * + * One can choose the toolchain to use for compiling the MAIN sources and/or compiling + * and running the TEST sources. These options apply to Java, Kotlin and Groovy sources + * when available. + * {@code "./gradlew check -PmainToolchain=8 -PtestToolchain=11"} will use: + *

+ * + * Gradle will automatically detect JDK distributions in well-known locations. + * The following command will list the detected JDKs on the host. + * {@code + * $ ./gradlew -q javaToolchains + * } + * + * We can also configure ENV variables and let Gradle know about them: + * {@code + * $ echo JDK11 + * /opt/openjdk/java11 + * $ echo JDK15 + * /opt/openjdk/java15 + * $ ./gradlew -Dorg.gradle.java.installations.fromEnv=JDK11,JDK15 check + * } + * + * @author Brian Clozel + */ + +plugins.withType(JavaPlugin) { + // Configure the Java Toolchain if the 'mainToolchain' property is defined + if (project.hasProperty('mainToolchain') && project.mainToolchain) { + def mainLanguageVersion = JavaLanguageVersion.of(project.mainToolchain.toString()) + java { + toolchain { + languageVersion = mainLanguageVersion + } + } + } + else { + // Fallback to JDK8 + java { + sourceCompatibility = JavaVersion.VERSION_1_8 + } + } + // Configure a specific Java Toolchain for compiling and running tests if the 'testToolchain' property is defined + if (project.hasProperty('testToolchain') && project.testToolchain) { + def testLanguageVersion = JavaLanguageVersion.of(project.testToolchain.toString()); + tasks.withType(JavaCompile).matching { it.name.contains("Test") }.configureEach { + javaCompiler = javaToolchains.compilerFor { + languageVersion = testLanguageVersion + } + } + tasks.withType(Test).configureEach{ + javaLauncher = javaToolchains.launcherFor { + languageVersion = testLanguageVersion + } + } + } +} + +plugins.withType(GroovyPlugin) { + // Fallback to JDK8 + if (!project.hasProperty('mainToolchain')) { + compileGroovy { + sourceCompatibility = JavaVersion.VERSION_1_8 + } + } +} + +// Configure the Kotlin compiler if the 'mainToolchain' property is defined +pluginManager.withPlugin("kotlin") { + if (project.hasProperty('mainToolchain') && project.mainToolchain) { + def mainLanguageVersion = JavaLanguageVersion.of(project.mainToolchain.toString()); + def compiler = javaToolchains.compilerFor { + languageVersion = mainLanguageVersion + } + // See https://kotlinlang.org/docs/gradle.html#attributes-specific-for-jvm + def javaVersion = mainLanguageVersion.toString() == '8' ? '1.8' : mainLanguageVersion.toString() + compileKotlin { + kotlinOptions { + jvmTarget = javaVersion + jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath + } + } + // Compile the test classes with the same version, 'testToolchain' will override if defined + compileTestKotlin { + kotlinOptions { + jvmTarget = javaVersion + jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath + } + } + } + else { + // Fallback to JDK8 + compileKotlin { + kotlinOptions { + jvmTarget = '1.8' + } + } + compileTestKotlin { + kotlinOptions { + jvmTarget = '1.8' + } + } + } + + if (project.hasProperty('testToolchain') && project.testToolchain) { + def testLanguageVersion = JavaLanguageVersion.of(project.testToolchain.toString()); + def compiler = javaToolchains.compilerFor { + languageVersion = testLanguageVersion + } + // See https://kotlinlang.org/docs/gradle.html#attributes-specific-for-jvm + def javaVersion = testLanguageVersion.toString() == '8' ? '1.8' : testLanguageVersion.toString() + compileTestKotlin { + kotlinOptions { + jvmTarget = javaVersion + jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath + } + } + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf016b3..e708b1c023ec 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5028f28f8e47..442d9132ea32 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 83f2acfdc319..4f906e0c811f 100755 --- a/gradlew +++ b/gradlew @@ -82,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -129,6 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -154,19 +156,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -175,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 24467a141f79..ac1b06f93825 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -51,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -61,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/import-into-eclipse.md b/import-into-eclipse.md index d303e0c7412b..bb665d788a13 100644 --- a/import-into-eclipse.md +++ b/import-into-eclipse.md @@ -3,12 +3,12 @@ This document will guide you through the process of importing the Spring Framework projects into Eclipse or the Spring Tool Suite (_STS_). It is recommended that you have a recent version of Eclipse. As a bare minimum you will need Eclipse with full Java -8 support, the Kotlin plugin, and the Groovy plugin. +8 support, Eclipse Buildship, the Kotlin plugin, and the Groovy plugin. The following instructions have been tested against [STS](https://spring.io/tools) 4.3.2 ([download](https://github.com/spring-projects/sts4/wiki/Previous-Versions#spring-tools-432-changelog)) -with [Eclipse Buildship](https://projects.eclipse.org/projects/tools.buildship). The -instructions should work with the latest Eclipse distribution as long as you install +(based on Eclipse 4.12) with [Eclipse Buildship](https://projects.eclipse.org/projects/tools.buildship). +The instructions should work with the latest Eclipse distribution as long as you install [Buildship](https://marketplace.eclipse.org/content/buildship-gradle-integration). Note that STS 4 comes with Buildship preinstalled. @@ -23,7 +23,7 @@ _When instructed to execute `./gradlew` from the command line, be sure to execut 1. Switch to Groovy 2.5 (Preferences -> Groovy -> Compiler -> Switch to 2.5...) in Eclipse. 1. Change the _Forbidden reference (access rule)_ in Eclipse from Error to Warning (Preferences -> Java -> Compiler -> Errors/Warnings -> Deprecated and restricted API -> Forbidden reference (access rule)). -1. Optionally install the [AspectJ Development Tools](https://www.eclipse.org/ajdt/downloads/) (_AJDT_) if you need to work with the `spring-aspects` project. +1. Optionally install the [AspectJ Development Tools](https://marketplace.eclipse.org/content/aspectj-development-tools) (_AJDT_) if you need to work with the `spring-aspects` project. The AspectJ Development Tools available in the Eclipse Marketplace have been tested with these instructions using STS 4.5 (Eclipse 4.14). 1. Optionally install the [TestNG plugin](https://testng.org/doc/eclipse.html) in Eclipse if you need to execute TestNG tests in the `spring-test` module. 1. Build `spring-oxm` from the command line with `./gradlew :spring-oxm:check`. 1. To apply project specific settings, run `./gradlew eclipseBuildship` from the command line. diff --git a/integration-tests/integration-tests.gradle b/integration-tests/integration-tests.gradle index ab3bba7354b6..71e23b906049 100644 --- a/integration-tests/integration-tests.gradle +++ b/integration-tests/integration-tests.gradle @@ -1,12 +1,14 @@ description = "Spring Integration Tests" -apply plugin: "org.springframework.build.test-sources" - dependencies { testCompile(project(":spring-aop")) testCompile(project(":spring-beans")) testCompile(project(":spring-context")) testCompile(project(":spring-core")) + testCompile(testFixtures(project(":spring-aop"))) + testCompile(testFixtures(project(":spring-beans"))) + testCompile(testFixtures(project(":spring-core"))) + testCompile(testFixtures(project(":spring-tx"))) testCompile(project(":spring-expression")) testCompile(project(":spring-jdbc")) testCompile(project(":spring-orm")) diff --git a/integration-tests/src/test/java/.gitignore b/integration-tests/src/test/java/.gitignore deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerAdviceOrderIntegrationTests.java b/integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerAdviceOrderIntegrationTests.java new file mode 100644 index 000000000000..da32ff120181 --- /dev/null +++ b/integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerAdviceOrderIntegrationTests.java @@ -0,0 +1,119 @@ +/* + * 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.aop.config; + +import java.util.ArrayList; +import java.util.List; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +/** + * Integration tests for advice invocation order for advice configured via the + * AOP namespace. + * + * @author Sam Brannen + * @since 5.2.7 + * @see org.springframework.aop.framework.autoproxy.AspectJAutoProxyAdviceOrderIntegrationTests + */ +class AopNamespaceHandlerAdviceOrderIntegrationTests { + + @Nested + @SpringJUnitConfig(locations = "AopNamespaceHandlerAdviceOrderIntegrationTests-afterFirst.xml") + @DirtiesContext + class AfterAdviceFirstTests { + + @Test + void afterAdviceIsInvokedFirst(@Autowired Echo echo, @Autowired InvocationTrackingAspect aspect) throws Exception { + assertThat(aspect.invocations).isEmpty(); + assertThat(echo.echo(42)).isEqualTo(42); + assertThat(aspect.invocations).containsExactly("around - start", "before", "around - end", "after", "after returning"); + + aspect.invocations.clear(); + assertThatExceptionOfType(Exception.class).isThrownBy(() -> echo.echo(new Exception())); + assertThat(aspect.invocations).containsExactly("around - start", "before", "around - end", "after", "after throwing"); + } + } + + @Nested + @SpringJUnitConfig(locations = "AopNamespaceHandlerAdviceOrderIntegrationTests-afterLast.xml") + @DirtiesContext + class AfterAdviceLastTests { + + @Test + void afterAdviceIsInvokedLast(@Autowired Echo echo, @Autowired InvocationTrackingAspect aspect) throws Exception { + assertThat(aspect.invocations).isEmpty(); + assertThat(echo.echo(42)).isEqualTo(42); + assertThat(aspect.invocations).containsExactly("around - start", "before", "around - end", "after returning", "after"); + + aspect.invocations.clear(); + assertThatExceptionOfType(Exception.class).isThrownBy(() -> echo.echo(new Exception())); + assertThat(aspect.invocations).containsExactly("around - start", "before", "around - end", "after throwing", "after"); + } + } + + + static class Echo { + + Object echo(Object obj) throws Exception { + if (obj instanceof Exception) { + throw (Exception) obj; + } + return obj; + } + } + + static class InvocationTrackingAspect { + + List invocations = new ArrayList<>(); + + Object around(ProceedingJoinPoint joinPoint) throws Throwable { + invocations.add("around - start"); + try { + return joinPoint.proceed(); + } + finally { + invocations.add("around - end"); + } + } + + void before() { + invocations.add("before"); + } + + void afterReturning() { + invocations.add("after returning"); + } + + void afterThrowing() { + invocations.add("after throwing"); + } + + void after() { + invocations.add("after"); + } + } + +} diff --git a/integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests.java b/integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests.java index 29f6e050812e..b4c1131ea1cd 100644 --- a/integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests.java +++ b/integration-tests/src/test/java/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests.java @@ -21,12 +21,12 @@ import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.core.testfixture.io.SerializationTestUtils; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpSession; import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.util.SerializationTestUtils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -70,7 +70,7 @@ void testSingletonScoping() throws Exception { assertThat(singletonScoped.getName()).isEqualTo(rob); singletonScoped.setName(bram); assertThat(singletonScoped.getName()).isEqualTo(bram); - ITestBean deserialized = (ITestBean) SerializationTestUtils.serializeAndDeserialize(singletonScoped); + ITestBean deserialized = SerializationTestUtils.serializeAndDeserialize(singletonScoped); assertThat(deserialized.getName()).isEqualTo(bram); } diff --git a/integration-tests/src/test/java/org/springframework/aop/framework/autoproxy/AdvisorAutoProxyCreatorIntegrationTests.java b/integration-tests/src/test/java/org/springframework/aop/framework/autoproxy/AdvisorAutoProxyCreatorIntegrationTests.java index e8d30cd33b96..cf067e01415c 100644 --- a/integration-tests/src/test/java/org/springframework/aop/framework/autoproxy/AdvisorAutoProxyCreatorIntegrationTests.java +++ b/integration-tests/src/test/java/org/springframework/aop/framework/autoproxy/AdvisorAutoProxyCreatorIntegrationTests.java @@ -26,17 +26,17 @@ import org.springframework.aop.support.AopUtils; import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor; +import org.springframework.aop.testfixture.advice.CountingBeforeAdvice; +import org.springframework.aop.testfixture.advice.MethodCounter; +import org.springframework.aop.testfixture.interceptor.NopInterceptor; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.testfixture.beans.ITestBean; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.lang.Nullable; -import org.springframework.tests.aop.advice.CountingBeforeAdvice; -import org.springframework.tests.aop.advice.MethodCounter; -import org.springframework.tests.aop.interceptor.NopInterceptor; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.transaction.CallCountingTransactionManager; import org.springframework.transaction.NoTransactionException; import org.springframework.transaction.interceptor.TransactionInterceptor; +import org.springframework.transaction.testfixture.CallCountingTransactionManager; import static org.assertj.core.api.Assertions.assertThat; diff --git a/integration-tests/src/test/java/org/springframework/aop/framework/autoproxy/AspectJAutoProxyAdviceOrderIntegrationTests.java b/integration-tests/src/test/java/org/springframework/aop/framework/autoproxy/AspectJAutoProxyAdviceOrderIntegrationTests.java new file mode 100644 index 000000000000..78d45922be0c --- /dev/null +++ b/integration-tests/src/test/java/org/springframework/aop/framework/autoproxy/AspectJAutoProxyAdviceOrderIntegrationTests.java @@ -0,0 +1,233 @@ +/* + * 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.aop.framework.autoproxy; + +import java.util.ArrayList; +import java.util.List; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +/** + * Integration tests for advice invocation order for advice configured via + * AspectJ auto-proxy support. + * + * @author Sam Brannen + * @since 5.2.7 + * @see org.springframework.aop.config.AopNamespaceHandlerAdviceOrderIntegrationTests + */ +class AspectJAutoProxyAdviceOrderIntegrationTests { + + /** + * {@link After @After} advice declared as first after method in source code. + */ + @Nested + @SpringJUnitConfig(AfterAdviceFirstConfig.class) + @DirtiesContext + class AfterAdviceFirstTests { + + @Test + void afterAdviceIsInvokedLast(@Autowired Echo echo, @Autowired AfterAdviceFirstAspect aspect) throws Exception { + assertThat(aspect.invocations).isEmpty(); + assertThat(echo.echo(42)).isEqualTo(42); + assertThat(aspect.invocations).containsExactly("around - start", "before", "after returning", "after", "around - end"); + + aspect.invocations.clear(); + assertThatExceptionOfType(Exception.class).isThrownBy( + () -> echo.echo(new Exception())); + assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end"); + } + } + + + /** + * This test class uses {@link AfterAdviceLastAspect} which declares its + * {@link After @After} advice as the last after advice type method + * in its source code. + * + *

On Java versions prior to JDK 7, we would have expected the {@code @After} + * advice method to be invoked before {@code @AfterThrowing} and + * {@code @AfterReturning} advice methods due to the AspectJ precedence + * rules implemented in + * {@link org.springframework.aop.aspectj.autoproxy.AspectJPrecedenceComparator}. + */ + @Nested + @SpringJUnitConfig(AfterAdviceLastConfig.class) + @DirtiesContext + class AfterAdviceLastTests { + + @Test + void afterAdviceIsInvokedLast(@Autowired Echo echo, @Autowired AfterAdviceLastAspect aspect) throws Exception { + assertThat(aspect.invocations).isEmpty(); + assertThat(echo.echo(42)).isEqualTo(42); + assertThat(aspect.invocations).containsExactly("around - start", "before", "after returning", "after", "around - end"); + + aspect.invocations.clear(); + assertThatExceptionOfType(Exception.class).isThrownBy( + () -> echo.echo(new Exception())); + assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end"); + } + } + + + @Configuration + @EnableAspectJAutoProxy(proxyTargetClass = true) + static class AfterAdviceFirstConfig { + + @Bean + AfterAdviceFirstAspect echoAspect() { + return new AfterAdviceFirstAspect(); + } + + @Bean + Echo echo() { + return new Echo(); + } + } + + @Configuration + @EnableAspectJAutoProxy(proxyTargetClass = true) + static class AfterAdviceLastConfig { + + @Bean + AfterAdviceLastAspect echoAspect() { + return new AfterAdviceLastAspect(); + } + + @Bean + Echo echo() { + return new Echo(); + } + } + + static class Echo { + + Object echo(Object obj) throws Exception { + if (obj instanceof Exception) { + throw (Exception) obj; + } + return obj; + } + } + + /** + * {@link After @After} advice declared as first after method in source code. + */ + @Aspect + static class AfterAdviceFirstAspect { + + List invocations = new ArrayList<>(); + + @Pointcut("execution(* echo(*))") + void echo() { + } + + @After("echo()") + void after() { + invocations.add("after"); + } + + @AfterReturning("echo()") + void afterReturning() { + invocations.add("after returning"); + } + + @AfterThrowing("echo()") + void afterThrowing() { + invocations.add("after throwing"); + } + + @Before("echo()") + void before() { + invocations.add("before"); + } + + @Around("echo()") + Object around(ProceedingJoinPoint joinPoint) throws Throwable { + invocations.add("around - start"); + try { + return joinPoint.proceed(); + } + finally { + invocations.add("around - end"); + } + } + } + + /** + * {@link After @After} advice declared as last after method in source code. + */ + @Aspect + static class AfterAdviceLastAspect { + + List invocations = new ArrayList<>(); + + @Pointcut("execution(* echo(*))") + void echo() { + } + + @Around("echo()") + Object around(ProceedingJoinPoint joinPoint) throws Throwable { + invocations.add("around - start"); + try { + return joinPoint.proceed(); + } + finally { + invocations.add("around - end"); + } + } + + @Before("echo()") + void before() { + invocations.add("before"); + } + + @AfterReturning("echo()") + void afterReturning() { + invocations.add("after returning"); + } + + @AfterThrowing("echo()") + void afterThrowing() { + invocations.add("after throwing"); + } + + @After("echo()") + void after() { + invocations.add("after"); + } + } + +} diff --git a/integration-tests/src/test/java/org/springframework/scheduling/annotation/ScheduledAndTransactionalAnnotationIntegrationTests.java b/integration-tests/src/test/java/org/springframework/scheduling/annotation/ScheduledAndTransactionalAnnotationIntegrationTests.java index 95d897138f45..16db4fc393a1 100644 --- a/integration-tests/src/test/java/org/springframework/scheduling/annotation/ScheduledAndTransactionalAnnotationIntegrationTests.java +++ b/integration-tests/src/test/java/org/springframework/scheduling/annotation/ScheduledAndTransactionalAnnotationIntegrationTests.java @@ -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. @@ -28,19 +28,19 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.testfixture.EnabledForTestGroups; import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.stereotype.Repository; -import org.springframework.tests.EnabledForTestGroups; -import org.springframework.tests.transaction.CallCountingTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.testfixture.CallCountingTransactionManager; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.Mockito.mock; -import static org.springframework.tests.TestGroup.PERFORMANCE; +import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING; /** * Integration tests cornering bug SPR-8651, which revealed that @Scheduled methods may @@ -52,7 +52,7 @@ * @since 3.1 */ @SuppressWarnings("resource") -@EnabledForTestGroups(PERFORMANCE) +@EnabledForTestGroups(LONG_RUNNING) class ScheduledAndTransactionalAnnotationIntegrationTests { @Test @@ -61,7 +61,7 @@ void failsWhenJdkProxyAndScheduledMethodNotPresentOnInterface() { ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigA.class); assertThatExceptionOfType(BeanCreationException.class) .isThrownBy(ctx::refresh) - .satisfies(ex -> assertThat(ex.getRootCause()).isInstanceOf(IllegalStateException.class)); + .withCauseInstanceOf(IllegalStateException.class); } @Test @@ -182,7 +182,7 @@ static MyAspect myAspect() { @Aspect public static class MyAspect { - private final AtomicInteger count = new AtomicInteger(0); + private final AtomicInteger count = new AtomicInteger(); @org.aspectj.lang.annotation.Before("execution(* scheduled())") public void checkTransaction() { @@ -200,7 +200,7 @@ public interface MyRepository { @Repository static class MyRepositoryImpl implements MyRepository { - private final AtomicInteger count = new AtomicInteger(0); + private final AtomicInteger count = new AtomicInteger(); @Transactional @Scheduled(fixedDelay = 5) @@ -226,7 +226,7 @@ public interface MyRepositoryWithScheduledMethod { @Repository static class MyRepositoryWithScheduledMethodImpl implements MyRepositoryWithScheduledMethod { - private final AtomicInteger count = new AtomicInteger(0); + private final AtomicInteger count = new AtomicInteger(); @Autowired(required = false) private MyAspect myAspect; diff --git a/integration-tests/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementIntegrationTests.java b/integration-tests/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementIntegrationTests.java index 20dfe81d2f86..8ba437386b04 100644 --- a/integration-tests/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementIntegrationTests.java +++ b/integration-tests/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementIntegrationTests.java @@ -40,9 +40,9 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.stereotype.Repository; -import org.springframework.tests.transaction.CallCountingTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor; +import org.springframework.transaction.testfixture.CallCountingTransactionManager; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; diff --git a/integration-tests/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerAdviceOrderIntegrationTests-afterFirst.xml b/integration-tests/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerAdviceOrderIntegrationTests-afterFirst.xml new file mode 100644 index 000000000000..fd98a0c1b407 --- /dev/null +++ b/integration-tests/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerAdviceOrderIntegrationTests-afterFirst.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + diff --git a/integration-tests/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerAdviceOrderIntegrationTests-afterLast.xml b/integration-tests/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerAdviceOrderIntegrationTests-afterLast.xml new file mode 100644 index 000000000000..bcab0df678a4 --- /dev/null +++ b/integration-tests/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerAdviceOrderIntegrationTests-afterLast.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + diff --git a/integration-tests/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests-context.xml b/integration-tests/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests-context.xml index 56d265dc06fe..ab891535b514 100644 --- a/integration-tests/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests-context.xml +++ b/integration-tests/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerScopeIntegrationTests-context.xml @@ -11,19 +11,19 @@ - + - + - + - + diff --git a/integration-tests/src/test/resources/org/springframework/aop/framework/autoproxy/AdvisorAutoProxyCreatorIntegrationTests-context.xml b/integration-tests/src/test/resources/org/springframework/aop/framework/autoproxy/AdvisorAutoProxyCreatorIntegrationTests-context.xml index 7c2adb9e0120..90ea7dc1f663 100644 --- a/integration-tests/src/test/resources/org/springframework/aop/framework/autoproxy/AdvisorAutoProxyCreatorIntegrationTests-context.xml +++ b/integration-tests/src/test/resources/org/springframework/aop/framework/autoproxy/AdvisorAutoProxyCreatorIntegrationTests-context.xml @@ -34,7 +34,7 @@ - + @@ -74,20 +74,20 @@ - + - + - org.springframework.tests.sample.beans.ITestBean.getName + org.springframework.beans.testfixture.beans.ITestBean.getName - + - + 4 @@ -97,7 +97,7 @@ - + diff --git a/settings.gradle b/settings.gradle index 923271aace3c..8c1472350f11 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,14 +5,17 @@ pluginManagement { } } -apply from: "$rootDir/gradle/build-cache-settings.gradle" +plugins { + id "com.gradle.enterprise" version "3.6.1" + id "io.spring.ge.conventions" version "0.0.7" +} include "spring-aop" include "spring-aspects" include "spring-beans" include "spring-context" -include "spring-context-support" include "spring-context-indexer" +include "spring-context-support" include "spring-core" include "kotlin-coroutines" project(':kotlin-coroutines').projectDir = file('spring-core/kotlin-coroutines') @@ -24,11 +27,12 @@ include "spring-jms" include "spring-messaging" include "spring-orm" include "spring-oxm" +include "spring-r2dbc" include "spring-test" include "spring-tx" include "spring-web" -include "spring-webmvc" include "spring-webflux" +include "spring-webmvc" include "spring-websocket" include "framework-bom" include "integration-tests" @@ -37,3 +41,24 @@ rootProject.name = "spring" rootProject.children.each {project -> project.buildFileName = "${project.name}.gradle" } + +settings.gradle.projectsLoaded { + gradleEnterprise { + buildScan { + if (settings.gradle.rootProject.hasProperty('mainToolchain')) { + value("Main toolchain", 'JDK' + settings.gradle.rootProject.getProperty('mainToolchain')) + } + if (settings.gradle.rootProject.hasProperty('testToolchain')) { + value("Test toolchain", 'JDK' + settings.gradle.rootProject.getProperty('testToolchain')) + } + File buildDir = settings.gradle.rootProject.getBuildDir() + buildDir.mkdirs() + new File(buildDir, "build-scan-uri.txt").text = "(build scan not generated)" + buildScanPublished { scan -> + if (buildDir.exists()) { + new File(buildDir, "build-scan-uri.txt").text = "${scan.buildScanUri}\n" + } + } + } + } +} diff --git a/spring-aop/spring-aop.gradle b/spring-aop/spring-aop.gradle index e4c20542e2cd..73bb378f39a7 100644 --- a/spring-aop/spring-aop.gradle +++ b/spring-aop/spring-aop.gradle @@ -6,4 +6,7 @@ dependencies { optional("org.aspectj:aspectjweaver") optional("org.apache.commons:commons-pool2") optional("com.jamonapi:jamon") + testCompile(testFixtures(project(":spring-beans"))) + testCompile(testFixtures(project(":spring-core"))) + testFixturesImplementation(testFixtures(project(":spring-core"))) } diff --git a/spring-aop/src/main/java/org/aopalliance/intercept/ConstructorInterceptor.java b/spring-aop/src/main/java/org/aopalliance/intercept/ConstructorInterceptor.java index 00a42801142e..8ac8f65ca3fa 100644 --- a/spring-aop/src/main/java/org/aopalliance/intercept/ConstructorInterceptor.java +++ b/spring-aop/src/main/java/org/aopalliance/intercept/ConstructorInterceptor.java @@ -16,6 +16,8 @@ package org.aopalliance.intercept; +import javax.annotation.Nonnull; + /** * Intercepts the construction of a new object. * @@ -54,6 +56,7 @@ public interface ConstructorInterceptor extends Interceptor { * @throws Throwable if the interceptors or the target object * throws an exception */ + @Nonnull Object construct(ConstructorInvocation invocation) throws Throwable; } diff --git a/spring-aop/src/main/java/org/aopalliance/intercept/ConstructorInvocation.java b/spring-aop/src/main/java/org/aopalliance/intercept/ConstructorInvocation.java index 867925b06582..72951383e959 100644 --- a/spring-aop/src/main/java/org/aopalliance/intercept/ConstructorInvocation.java +++ b/spring-aop/src/main/java/org/aopalliance/intercept/ConstructorInvocation.java @@ -18,6 +18,8 @@ import java.lang.reflect.Constructor; +import javax.annotation.Nonnull; + /** * Description of an invocation to a constructor, given to an * interceptor upon constructor-call. @@ -36,6 +38,7 @@ public interface ConstructorInvocation extends Invocation { * {@link Joinpoint#getStaticPart()} method (same result). * @return the constructor being called */ + @Nonnull Constructor getConstructor(); } diff --git a/spring-aop/src/main/java/org/aopalliance/intercept/Invocation.java b/spring-aop/src/main/java/org/aopalliance/intercept/Invocation.java index 69db081b1cc3..96caaefefe00 100644 --- a/spring-aop/src/main/java/org/aopalliance/intercept/Invocation.java +++ b/spring-aop/src/main/java/org/aopalliance/intercept/Invocation.java @@ -16,6 +16,8 @@ package org.aopalliance.intercept; +import javax.annotation.Nonnull; + /** * This interface represents an invocation in the program. * @@ -32,6 +34,7 @@ public interface Invocation extends Joinpoint { * array to change the arguments. * @return the argument of the invocation */ + @Nonnull Object[] getArguments(); } diff --git a/spring-aop/src/main/java/org/aopalliance/intercept/Joinpoint.java b/spring-aop/src/main/java/org/aopalliance/intercept/Joinpoint.java index 356087876396..780275e9782c 100644 --- a/spring-aop/src/main/java/org/aopalliance/intercept/Joinpoint.java +++ b/spring-aop/src/main/java/org/aopalliance/intercept/Joinpoint.java @@ -18,6 +18,9 @@ import java.lang.reflect.AccessibleObject; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + /** * This interface represents a generic runtime joinpoint (in the AOP * terminology). @@ -46,6 +49,7 @@ public interface Joinpoint { * @return see the children interfaces' proceed definition * @throws Throwable if the joinpoint throws an exception */ + @Nullable Object proceed() throws Throwable; /** @@ -53,6 +57,7 @@ public interface Joinpoint { *

For instance, the target object for an invocation. * @return the object (can be null if the accessible object is static) */ + @Nullable Object getThis(); /** @@ -60,6 +65,7 @@ public interface Joinpoint { *

The static part is an accessible object on which a chain of * interceptors are installed. */ + @Nonnull AccessibleObject getStaticPart(); } diff --git a/spring-aop/src/main/java/org/aopalliance/intercept/MethodInterceptor.java b/spring-aop/src/main/java/org/aopalliance/intercept/MethodInterceptor.java index 3033b8b5fbc0..9188e25e1d0d 100644 --- a/spring-aop/src/main/java/org/aopalliance/intercept/MethodInterceptor.java +++ b/spring-aop/src/main/java/org/aopalliance/intercept/MethodInterceptor.java @@ -16,6 +16,9 @@ package org.aopalliance.intercept; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + /** * Intercepts calls on an interface on its way to the target. These * are nested "on top" of the target. @@ -52,6 +55,7 @@ public interface MethodInterceptor extends Interceptor { * @throws Throwable if the interceptors or the target object * throws an exception */ - Object invoke(MethodInvocation invocation) throws Throwable; + @Nullable + Object invoke(@Nonnull MethodInvocation invocation) throws Throwable; } diff --git a/spring-aop/src/main/java/org/aopalliance/intercept/MethodInvocation.java b/spring-aop/src/main/java/org/aopalliance/intercept/MethodInvocation.java index 3d73f3d12f04..f1f511bea4cb 100644 --- a/spring-aop/src/main/java/org/aopalliance/intercept/MethodInvocation.java +++ b/spring-aop/src/main/java/org/aopalliance/intercept/MethodInvocation.java @@ -18,6 +18,8 @@ import java.lang.reflect.Method; +import javax.annotation.Nonnull; + /** * Description of an invocation to a method, given to an interceptor * upon method-call. @@ -36,6 +38,7 @@ public interface MethodInvocation extends Invocation { * {@link Joinpoint#getStaticPart()} method (same result). * @return the method being called */ + @Nonnull Method getMethod(); } diff --git a/spring-aop/src/main/java/org/springframework/aop/AfterReturningAdvice.java b/spring-aop/src/main/java/org/springframework/aop/AfterReturningAdvice.java index b3b2c0a9d9bd..8c2c5d6ef8f0 100644 --- a/spring-aop/src/main/java/org/springframework/aop/AfterReturningAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/AfterReturningAdvice.java @@ -33,9 +33,9 @@ public interface AfterReturningAdvice extends AfterAdvice { /** * Callback after a given method successfully returned. * @param returnValue the value returned by the method, if any - * @param method method being invoked - * @param args arguments to the method - * @param target target of the method invocation. May be {@code null}. + * @param method the method being invoked + * @param args the arguments to the method + * @param target the target of the method invocation. May be {@code null}. * @throws Throwable if this object wishes to abort the call. * Any exception thrown will be returned to the caller if it's * allowed by the method signature. Otherwise the exception diff --git a/spring-aop/src/main/java/org/springframework/aop/DynamicIntroductionAdvice.java b/spring-aop/src/main/java/org/springframework/aop/DynamicIntroductionAdvice.java index 08c704857f75..2f46775b9459 100644 --- a/spring-aop/src/main/java/org/springframework/aop/DynamicIntroductionAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/DynamicIntroductionAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ *

Introductions are often mixins, enabling the building of composite * objects that can achieve many of the goals of multiple inheritance in Java. * - *

Compared to {qlink IntroductionInfo}, this interface allows an advice to + *

Compared to {@link IntroductionInfo}, this interface allows an advice to * implement a range of interfaces that is not necessarily known in advance. * Thus an {@link IntroductionAdvisor} can be used to specify which interfaces * will be exposed in an advised object. diff --git a/spring-aop/src/main/java/org/springframework/aop/MethodBeforeAdvice.java b/spring-aop/src/main/java/org/springframework/aop/MethodBeforeAdvice.java index ae791a21b04c..806744d09c31 100644 --- a/spring-aop/src/main/java/org/springframework/aop/MethodBeforeAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/MethodBeforeAdvice.java @@ -32,9 +32,9 @@ public interface MethodBeforeAdvice extends BeforeAdvice { /** * Callback before a given method is invoked. - * @param method method being invoked - * @param args arguments to the method - * @param target target of the method invocation. May be {@code null}. + * @param method the method being invoked + * @param args the arguments to the method + * @param target the target of the method invocation. May be {@code null}. * @throws Throwable if this object wishes to abort the call. * Any exception thrown will be returned to the caller if it's * allowed by the method signature. Otherwise the exception diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java index 32b691f4410b..8ef1bf148a17 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java @@ -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 @@ protected Class getDiscoveredThrowingType() { 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 @@ protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable } try { ReflectionUtils.makeAccessible(this.aspectJAdviceMethod); - // TODO AopUtils.invokeJoinpointUsingReflection return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs); } catch (IllegalArgumentException ex) { diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java index 1dacc08948b7..ffcea9d0b0c7 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java @@ -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. @@ -470,22 +470,10 @@ else if (numAnnotationSlots == 1) { */ @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; } /** @@ -648,7 +636,7 @@ private PointcutBody getPointcutBody(String[] tokens, int startIndex) { } if (tokens[currentIndex].endsWith(")")) { - sb.append(tokens[currentIndex].substring(0, tokens[currentIndex].length() - 1)); + sb.append(tokens[currentIndex], 0, tokens[currentIndex].length() - 1); return new PointcutBody(numTokensConsumed, sb.toString().trim()); } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterAdvice.java index 46ad280a0352..a8081b461aa1 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 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. @@ -23,6 +23,7 @@ import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.AfterAdvice; +import org.springframework.lang.Nullable; /** * Spring AOP advice wrapping an AspectJ after advice method. @@ -42,6 +43,7 @@ public AspectJAfterAdvice( @Override + @Nullable public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterThrowingAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterThrowingAdvice.java index 7befd24cf7b6..953658d66e50 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterThrowingAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterThrowingAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 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. @@ -23,6 +23,7 @@ import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.AfterAdvice; +import org.springframework.lang.Nullable; /** * Spring AOP advice wrapping an AspectJ after-throwing advice method. @@ -57,6 +58,7 @@ public void setThrowingName(String name) { } @Override + @Nullable public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAroundAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAroundAdvice.java index 7eec99f42e8c..7a7029323d26 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAroundAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAroundAdvice.java @@ -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. @@ -25,6 +25,7 @@ import org.aspectj.weaver.tools.JoinPointMatch; import org.springframework.aop.ProxyMethodInvocation; +import org.springframework.lang.Nullable; /** * Spring AOP around advice (MethodInterceptor) that wraps @@ -60,6 +61,7 @@ protected boolean supportsProceedingJoinPoint() { } @Override + @Nullable public Object invoke(MethodInvocation mi) throws Throwable { if (!(mi instanceof ProxyMethodInvocation)) { throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJProxyUtils.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJProxyUtils.java index 833a109f131e..e161007abe98 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJProxyUtils.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJProxyUtils.java @@ -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 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 @@ private static boolean isAspectJAdvice(Advisor advisor) { ((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; + } + } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/MethodInvocationProceedingJoinPoint.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/MethodInvocationProceedingJoinPoint.java index 471647f89cf3..d25ec2eb6bc4 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/MethodInvocationProceedingJoinPoint.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/MethodInvocationProceedingJoinPoint.java @@ -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. @@ -84,11 +84,13 @@ public MethodInvocationProceedingJoinPoint(ProxyMethodInvocation methodInvocatio } @Override + @Nullable public Object proceed() throws Throwable { return this.methodInvocation.invocableClone().proceed(); } @Override + @Nullable public Object proceed(Object[] arguments) throws Throwable { Assert.notNull(arguments, "Argument array passed to proceed cannot be null"); if (arguments.length != this.methodInvocation.getArguments().length) { @@ -219,10 +221,12 @@ public Class[] getParameterTypes() { @Override @Nullable public String[] getParameterNames() { - if (this.parameterNames == null) { - this.parameterNames = parameterNameDiscoverer.getParameterNames(getMethod()); + String[] parameterNames = this.parameterNames; + if (parameterNames == null) { + parameterNames = parameterNameDiscoverer.getParameterNames(getMethod()); + this.parameterNames = parameterNames; } - return this.parameterNames; + return parameterNames; } @Override diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJProxyFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJProxyFactory.java index 521762fb0faa..ae9e41d6ad96 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJProxyFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJProxyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -160,23 +160,12 @@ private MetadataAwareAspectInstanceFactory createAspectInstanceFactory( } /** - * Get the singleton aspect instance for the supplied aspect type. An instance - * is created if one cannot be found in the instance cache. + * Get the singleton aspect instance for the supplied aspect type. + * An instance is created if one cannot be found in the instance cache. */ private Object getSingletonAspectInstance(Class aspectClass) { - // Quick check without a lock... - Object instance = aspectCache.get(aspectClass); - if (instance == null) { - synchronized (aspectCache) { - // To be safe, check within full lock now... - instance = aspectCache.get(aspectClass); - if (instance == null) { - instance = new SimpleAspectInstanceFactory(aspectClass).getAspectInstance(); - aspectCache.put(aspectClass, instance); - } - } - } - return instance; + return aspectCache.computeIfAbsent(aspectClass, + clazz -> new SimpleAspectInstanceFactory(clazz).getAspectInstance()); } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java index 9160c6480026..d5fefcec81d0 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java @@ -56,7 +56,7 @@ public class BeanFactoryAspectInstanceFactory implements MetadataAwareAspectInst * introspect to create AJType metadata using the type returned for the * given bean name from the BeanFactory. * @param beanFactory the BeanFactory to obtain instance(s) from - * @param name name of the bean + * @param name the name of the bean */ public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name) { this(beanFactory, name, null); diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectJAdvisorsBuilder.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectJAdvisorsBuilder.java index 85adb1daa7a4..8896f990ecbb 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectJAdvisorsBuilder.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectJAdvisorsBuilder.java @@ -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. @@ -97,7 +97,7 @@ public List buildAspectJAdvisors() { } // We must be careful not to instantiate beans eagerly as in this case they // would be cached by the Spring container but would not have been weaved. - Class beanType = this.beanFactory.getType(beanName); + Class beanType = this.beanFactory.getType(beanName, false); if (beanType == null) { continue; } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactory.java index 7bf8cec70540..c1c10c946ed6 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactory.java @@ -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. @@ -51,37 +51,47 @@ 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; /** * Factory that can create Spring AOP Advisors given AspectJ classes from - * classes honoring the AspectJ 5 annotation syntax, using reflection to - * invoke the corresponding advice methods. + * classes honoring AspectJ's annotation syntax, using reflection to invoke the + * corresponding advice methods. * * @author Rod Johnson * @author Adrian Colyer * @author Juergen Hoeller * @author Ramnivas Laddad * @author Phillip Webb + * @author Sam Brannen * @since 2.0 */ @SuppressWarnings("serial") public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable { - private static final Comparator 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 adviceMethodComparator; static { + // Note: although @After is ordered before @AfterReturning and @AfterThrowing, + // an @After advice method will actually be invoked after @AfterReturning and + // @AfterThrowing methods due to the fact that AspectJAfterAdvice.invoke(MethodInvocation) + // invokes proceed() in a `try` block and only invokes the @After advice method + // in a corresponding `finally` block. Comparator adviceKindComparator = new ConvertingComparator<>( new InstanceComparator<>( Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class), (Converter) method -> { - AspectJAnnotation annotation = - AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); - return (annotation != null ? annotation.getAnnotation() : null); + AspectJAnnotation ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); + return (ann != null ? ann.getAnnotation() : null); }); Comparator methodNameComparator = new ConvertingComparator<>(Method::getName); - METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator); + adviceMethodComparator = adviceKindComparator.thenComparing(methodNameComparator); } @@ -123,7 +133,15 @@ public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstan List advisors = new ArrayList<>(); for (Method method : getAdvisorMethods(aspectClass)) { - Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); + // Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect + // to getAdvisor(...) to represent the "current position" in the declared methods list. + // However, since Java 7 the "current position" is not valid since the JDK no longer + // returns declared methods in the order in which they are declared in the source code. + // Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods + // discovered via reflection in order to support reliable advice ordering across JVM launches. + // Specifically, a value of 0 aligns with the default value used in + // AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor). + Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName); if (advisor != null) { advisors.add(advisor); } @@ -147,14 +165,11 @@ public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstan } private List getAdvisorMethods(Class aspectClass) { - final List methods = new ArrayList<>(); - ReflectionUtils.doWithMethods(aspectClass, method -> { - // Exclude pointcuts - if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) { - methods.add(method); - } - }, ReflectionUtils.USER_DECLARED_METHODS); - methods.sort(METHOD_COMPARATOR); + List methods = new ArrayList<>(); + ReflectionUtils.doWithMethods(aspectClass, methods::add, adviceMethodFilter); + if (methods.size() > 1) { + methods.sort(adviceMethodComparator); + } return methods; } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/autoproxy/AspectJAwareAdvisorAutoProxyCreator.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/autoproxy/AspectJAwareAdvisorAutoProxyCreator.java index ea21644dc15c..2d2aabd07fae 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/autoproxy/AspectJAwareAdvisorAutoProxyCreator.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/autoproxy/AspectJAwareAdvisorAutoProxyCreator.java @@ -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. @@ -50,27 +50,27 @@ public class AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProx /** - * Sort the rest by AspectJ precedence. If two pieces of advice have - * come from the same aspect they will have the same order. - * Advice from the same aspect is then further ordered according to the + * Sort the supplied {@link Advisor} instances according to AspectJ precedence. + *

If two pieces of advice come from the same aspect, they will have the same + * order. Advice from the same aspect is then further ordered according to the * following rules: *

*

Important: Advisors are sorted in precedence order, from highest * precedence to lowest. "On the way in" to a join point, the highest precedence - * advisor should run first. "On the way out" of a join point, the highest precedence - * advisor should run last. + * advisor should run first. "On the way out" of a join point, the highest + * precedence advisor should run last. */ @Override - @SuppressWarnings("unchecked") protected List sortAdvisors(List advisors) { List partiallyComparableAdvisors = new ArrayList<>(advisors.size()); - for (Advisor element : advisors) { + for (Advisor advisor : advisors) { partiallyComparableAdvisors.add( - new PartiallyComparableAdvisorHolder(element, DEFAULT_PRECEDENCE_COMPARATOR)); + new PartiallyComparableAdvisorHolder(advisor, DEFAULT_PRECEDENCE_COMPARATOR)); } List sorted = PartialOrder.sort(partiallyComparableAdvisors); if (sorted != null) { @@ -86,8 +86,8 @@ protected List sortAdvisors(List advisors) { } /** - * Adds an {@link ExposeInvocationInterceptor} to the beginning of the advice chain. - * These additional advices are needed when using AspectJ expression pointcuts + * Add an {@link ExposeInvocationInterceptor} to the beginning of the advice chain. + *

This additional advice is needed when using AspectJ pointcut expressions * and when using AspectJ-style advice. */ @Override @@ -110,7 +110,7 @@ protected boolean shouldSkip(Class beanClass, String beanName) { /** - * Implements AspectJ PartialComparable interface for defining partial orderings. + * Implements AspectJ's {@link PartialComparable} interface for defining partial orderings. */ private static class PartiallyComparableAdvisorHolder implements PartialComparable { @@ -140,17 +140,19 @@ public Advisor getAdvisor() { @Override public String toString() { - StringBuilder sb = new StringBuilder(); Advice advice = this.advisor.getAdvice(); - sb.append(ClassUtils.getShortName(advice.getClass())); - sb.append(": "); + StringBuilder sb = new StringBuilder(ClassUtils.getShortName(advice.getClass())); + boolean appended = false; if (this.advisor instanceof Ordered) { - sb.append("order ").append(((Ordered) this.advisor).getOrder()).append(", "); + sb.append(": order = ").append(((Ordered) this.advisor).getOrder()); + appended = true; } if (advice instanceof AbstractAspectJAdvice) { + sb.append(!appended ? ": " : ", "); AbstractAspectJAdvice ajAdvice = (AbstractAspectJAdvice) advice; + sb.append("aspect name = "); sb.append(ajAdvice.getAspectName()); - sb.append(", declaration order "); + sb.append(", declaration order = "); sb.append(ajAdvice.getDeclarationOrder()); } return sb.toString(); diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/autoproxy/AspectJPrecedenceComparator.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/autoproxy/AspectJPrecedenceComparator.java index 64066f7c04e5..2d243fadc726 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/autoproxy/AspectJPrecedenceComparator.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/autoproxy/AspectJPrecedenceComparator.java @@ -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. @@ -27,20 +27,22 @@ /** * Orders AspectJ advice/advisors by precedence (not invocation order). * - *

Given two pieces of advice, {@code a} and {@code b}: + *

Given two pieces of advice, {@code A} and {@code B}: *

* - *

Important: Note that unlike a normal comparator a return of 0 means - * we don't care about the ordering, not that the two elements must be sorted - * identically. Used with AspectJ PartialOrder class. + *

Important: This comparator is used with AspectJ's + * {@link org.aspectj.util.PartialOrder PartialOrder} sorting utility. Thus, unlike + * a normal {@link Comparator}, a return value of {@code 0} from this comparator + * means we don't care about the ordering, not that the two elements must be sorted + * identically. * * @author Adrian Colyer * @author Juergen Hoeller @@ -59,16 +61,16 @@ class AspectJPrecedenceComparator implements Comparator { /** - * Create a default AspectJPrecedenceComparator. + * Create a default {@code AspectJPrecedenceComparator}. */ public AspectJPrecedenceComparator() { this.advisorComparator = AnnotationAwareOrderComparator.INSTANCE; } /** - * Create a AspectJPrecedenceComparator, using the given Comparator + * Create an {@code AspectJPrecedenceComparator}, using the given {@link Comparator} * for comparing {@link org.springframework.aop.Advisor} instances. - * @param advisorComparator the Comparator to use for Advisors + * @param advisorComparator the {@code Comparator} to use for advisors */ public AspectJPrecedenceComparator(Comparator advisorComparator) { Assert.notNull(advisorComparator, "Advisor comparator must not be null"); @@ -125,27 +127,21 @@ private boolean declaredInSameAspect(Advisor advisor1, Advisor advisor2) { getAspectName(advisor1).equals(getAspectName(advisor2))); } - private boolean hasAspectName(Advisor anAdvisor) { - return (anAdvisor instanceof AspectJPrecedenceInformation || - anAdvisor.getAdvice() instanceof AspectJPrecedenceInformation); + private boolean hasAspectName(Advisor advisor) { + return (advisor instanceof AspectJPrecedenceInformation || + advisor.getAdvice() instanceof AspectJPrecedenceInformation); } // pre-condition is that hasAspectName returned true - private String getAspectName(Advisor anAdvisor) { - AspectJPrecedenceInformation pi = AspectJAopUtils.getAspectJPrecedenceInformationFor(anAdvisor); - Assert.state(pi != null, "Unresolvable precedence information"); - return pi.getAspectName(); + private String getAspectName(Advisor advisor) { + AspectJPrecedenceInformation precedenceInfo = AspectJAopUtils.getAspectJPrecedenceInformationFor(advisor); + Assert.state(precedenceInfo != null, () -> "Unresolvable AspectJPrecedenceInformation for " + advisor); + return precedenceInfo.getAspectName(); } - private int getAspectDeclarationOrder(Advisor anAdvisor) { - AspectJPrecedenceInformation precedenceInfo = - AspectJAopUtils.getAspectJPrecedenceInformationFor(anAdvisor); - if (precedenceInfo != null) { - return precedenceInfo.getDeclarationOrder(); - } - else { - return 0; - } + private int getAspectDeclarationOrder(Advisor advisor) { + AspectJPrecedenceInformation precedenceInfo = AspectJAopUtils.getAspectJPrecedenceInformationFor(advisor); + return (precedenceInfo != null ? precedenceInfo.getDeclarationOrder() : 0); } } diff --git a/spring-aop/src/main/java/org/springframework/aop/config/AdviceEntry.java b/spring-aop/src/main/java/org/springframework/aop/config/AdviceEntry.java index f9869a5f9b10..7d9b2ad2dc82 100644 --- a/spring-aop/src/main/java/org/springframework/aop/config/AdviceEntry.java +++ b/spring-aop/src/main/java/org/springframework/aop/config/AdviceEntry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 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,13 +30,14 @@ public class AdviceEntry implements ParseState.Entry { /** - * Creates a new instance of the {@link AdviceEntry} class. - * @param kind the kind of advice represented by this entry (before, after, around, etc.) + * Create a new {@code AdviceEntry} instance. + * @param kind the kind of advice represented by this entry (before, after, around) */ public AdviceEntry(String kind) { this.kind = kind; } + @Override public String toString() { return "Advice (" + this.kind + ")"; diff --git a/spring-aop/src/main/java/org/springframework/aop/config/AdvisorEntry.java b/spring-aop/src/main/java/org/springframework/aop/config/AdvisorEntry.java index 1f7ba059620a..1a8b45c4823f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/config/AdvisorEntry.java +++ b/spring-aop/src/main/java/org/springframework/aop/config/AdvisorEntry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 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,13 +30,14 @@ public class AdvisorEntry implements ParseState.Entry { /** - * Creates a new instance of the {@link AdvisorEntry} class. + * Create a new {@code AdvisorEntry} instance. * @param name the bean name of the advisor */ public AdvisorEntry(String name) { this.name = name; } + @Override public String toString() { return "Advisor '" + this.name + "'"; diff --git a/spring-aop/src/main/java/org/springframework/aop/config/AopNamespaceHandler.java b/spring-aop/src/main/java/org/springframework/aop/config/AopNamespaceHandler.java index 99eb2fa6f594..fa6cc80a1f3c 100644 --- a/spring-aop/src/main/java/org/springframework/aop/config/AopNamespaceHandler.java +++ b/spring-aop/src/main/java/org/springframework/aop/config/AopNamespaceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 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. @@ -61,12 +61,12 @@ public class AopNamespaceHandler extends NamespaceHandlerSupport { */ @Override public void init() { - // In 2.0 XSD as well as in 2.1 XSD. + // In 2.0 XSD as well as in 2.5+ XSDs registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser()); registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator()); - // Only in 2.0 XSD: moved to context namespace as of 2.1 + // Only in 2.0 XSD: moved to context namespace in 2.5+ registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); } diff --git a/spring-aop/src/main/java/org/springframework/aop/config/AspectEntry.java b/spring-aop/src/main/java/org/springframework/aop/config/AspectEntry.java index 13633bc2a27c..2d4360048cf7 100644 --- a/spring-aop/src/main/java/org/springframework/aop/config/AspectEntry.java +++ b/spring-aop/src/main/java/org/springframework/aop/config/AspectEntry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 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. @@ -34,7 +34,7 @@ public class AspectEntry implements ParseState.Entry { /** - * Create a new AspectEntry. + * Create a new {@code AspectEntry} instance. * @param id the id of the aspect element * @param ref the bean name referenced by this aspect element */ @@ -43,6 +43,7 @@ public AspectEntry(String id, String ref) { this.ref = ref; } + @Override public String toString() { return "Aspect: " + (StringUtils.hasLength(this.id) ? "id='" + this.id + "'" : "ref='" + this.ref + "'"); diff --git a/spring-aop/src/main/java/org/springframework/aop/config/PointcutEntry.java b/spring-aop/src/main/java/org/springframework/aop/config/PointcutEntry.java index 950f8da387e7..e6066c513ee9 100644 --- a/spring-aop/src/main/java/org/springframework/aop/config/PointcutEntry.java +++ b/spring-aop/src/main/java/org/springframework/aop/config/PointcutEntry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 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. @@ -28,14 +28,16 @@ public class PointcutEntry implements ParseState.Entry { private final String name; + /** - * Creates a new instance of the {@link PointcutEntry} class. + * Create a new {@code PointcutEntry} instance. * @param name the bean name of the pointcut */ public PointcutEntry(String name) { this.name = name; } + @Override public String toString() { return "Pointcut '" + this.name + "'"; diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java b/spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java index a3a87f2117f8..d941bdac4c9c 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import org.springframework.aop.Advisor; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.core.SmartClassLoader; import org.springframework.lang.Nullable; /** @@ -89,7 +90,13 @@ public Object postProcessAfterInitialization(Object bean, String beanName) { } proxyFactory.addAdvisor(this.advisor); customizeProxyFactory(proxyFactory); - return proxyFactory.getProxy(getProxyClassLoader()); + + // Use original ClassLoader if bean class not locally loaded in overriding class loader + ClassLoader classLoader = getProxyClassLoader(); + if (classLoader instanceof SmartClassLoader && classLoader != bean.getClass().getClassLoader()) { + classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader(); + } + return proxyFactory.getProxy(classLoader); } // No proxy needed. diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/AbstractSingletonProxyFactoryBean.java b/spring-aop/src/main/java/org/springframework/aop/framework/AbstractSingletonProxyFactoryBean.java index 6882e97cc9fd..bb680a3477b5 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/AbstractSingletonProxyFactoryBean.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/AbstractSingletonProxyFactoryBean.java @@ -189,7 +189,7 @@ else if (!isProxyTargetClass()) { /** * Determine a TargetSource for the given target (or TargetSource). - * @param target target. If this is an implementation of TargetSource it is + * @param target the target. If this is an implementation of TargetSource it is * used as our TargetSource; otherwise it is wrapped in a SingletonTargetSource. * @return a TargetSource for this object */ diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/Advised.java b/spring-aop/src/main/java/org/springframework/aop/framework/Advised.java index 3deb11bb2a1d..b956f00fca2b 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/Advised.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/Advised.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -114,6 +114,15 @@ public interface Advised extends TargetClassAware { */ Advisor[] getAdvisors(); + /** + * Return the number of advisors applying to this proxy. + *

The default implementation delegates to {@code getAdvisors().length}. + * @since 5.3.1 + */ + default int getAdvisorCount() { + return getAdvisors().length; + } + /** * Add an advisor at the end of the advisor chain. *

The Advisor may be an {@link org.springframework.aop.IntroductionAdvisor}, @@ -142,7 +151,7 @@ public interface Advised extends TargetClassAware { /** * Remove the advisor at the given index. - * @param index index of advisor to remove + * @param index the index of advisor to remove * @throws AopConfigException if the index is invalid */ void removeAdvisor(int index) throws AopConfigException; @@ -177,7 +186,7 @@ public interface Advised extends TargetClassAware { *

Note that the given advice will apply to all invocations on the proxy, * even to the {@code toString()} method! Use appropriate advice implementations * or specify appropriate pointcuts to apply to a narrower set of methods. - * @param advice advice to add to the tail of the chain + * @param advice the advice to add to the tail of the chain * @throws AopConfigException in case of invalid advice * @see #addAdvice(int, Advice) * @see org.springframework.aop.support.DefaultPointcutAdvisor @@ -193,7 +202,7 @@ public interface Advised extends TargetClassAware { * even to the {@code toString()} method! Use appropriate advice implementations * or specify appropriate pointcuts to apply to a narrower set of methods. * @param pos index from 0 (head) - * @param advice advice to add at the specified position in the advice chain + * @param advice the advice to add at the specified position in the advice chain * @throws AopConfigException in case of invalid advice */ void addAdvice(int pos, Advice advice) throws AopConfigException; diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java b/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java index a82e77a0f1b9..5664cf7ffd55 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java @@ -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. @@ -95,12 +95,6 @@ public class AdvisedSupport extends ProxyConfig implements Advised { */ private List advisors = new ArrayList<>(); - /** - * Array updated on changes to the advisors list, which is easier - * to manipulate internally. - */ - private Advisor[] advisorArray = new Advisor[0]; - /** * No-arg constructor for use as a JavaBean. @@ -244,7 +238,12 @@ public boolean isInterfaceProxied(Class intf) { @Override public final Advisor[] getAdvisors() { - return this.advisorArray; + return this.advisors.toArray(new Advisor[0]); + } + + @Override + public int getAdvisorCount() { + return this.advisors.size(); } @Override @@ -283,17 +282,15 @@ public void removeAdvisor(int index) throws AopConfigException { "This configuration only has " + this.advisors.size() + " advisors."); } - Advisor advisor = this.advisors.get(index); + Advisor advisor = this.advisors.remove(index); if (advisor instanceof IntroductionAdvisor) { IntroductionAdvisor ia = (IntroductionAdvisor) advisor; // We need to remove introduction interfaces. - for (int j = 0; j < ia.getInterfaces().length; j++) { - removeInterface(ia.getInterfaces()[j]); + for (Class ifc : ia.getInterfaces()) { + removeInterface(ifc); } } - this.advisors.remove(index); - updateAdvisorArray(); adviceChanged(); } @@ -340,7 +337,6 @@ public void addAdvisors(Collection advisors) { Assert.notNull(advisor, "Advisor must not be null"); this.advisors.add(advisor); } - updateAdvisorArray(); adviceChanged(); } } @@ -364,27 +360,18 @@ private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigExcept "Illegal position " + pos + " in advisor list with size " + this.advisors.size()); } this.advisors.add(pos, advisor); - updateAdvisorArray(); adviceChanged(); } - /** - * Bring the array up to date with the list. - */ - protected final void updateAdvisorArray() { - this.advisorArray = this.advisors.toArray(new Advisor[0]); - } - /** * Allows uncontrolled access to the {@link List} of {@link Advisor Advisors}. - *

Use with care, and remember to {@link #updateAdvisorArray() refresh the advisor array} - * and {@link #adviceChanged() fire advice changed events} when making any modifications. + *

Use with care, and remember to {@link #adviceChanged() fire advice changed events} + * when making any modifications. */ protected final List getAdvisorsInternal() { return this.advisors; } - @Override public void addAdvice(Advice advice) throws AopConfigException { int pos = this.advisors.size(); @@ -522,7 +509,6 @@ protected void copyConfigurationFrom(AdvisedSupport other, TargetSource targetSo Assert.notNull(advisor, "Advisor must not be null"); this.advisors.add(advisor); } - updateAdvisorArray(); adviceChanged(); } @@ -537,7 +523,6 @@ AdvisedSupport getConfigurationOnlyCopy() { copy.advisorChainFactory = this.advisorChainFactory; copy.interfaces = this.interfaces; copy.advisors = this.advisors; - copy.updateAdvisorArray(); return copy; } @@ -554,7 +539,6 @@ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFound this.methodCache = new ConcurrentHashMap<>(32); } - @Override public String toProxyConfigString() { return toString(); diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/AopContext.java b/spring-aop/src/main/java/org/springframework/aop/framework/AopContext.java index 36b5635d4abf..9653ced6bc8b 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/AopContext.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/AopContext.java @@ -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. @@ -67,7 +67,8 @@ public static Object currentProxy() throws IllegalStateException { Object proxy = currentProxy.get(); if (proxy == null) { throw new IllegalStateException( - "Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available."); + "Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and " + + "ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context."); } return proxy; } diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/AopProxyUtils.java b/spring-aop/src/main/java/org/springframework/aop/framework/AopProxyUtils.java index a501d4614f27..5dd18747ef3a 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/AopProxyUtils.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/AopProxyUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -206,7 +206,7 @@ public static boolean equalsProxiedInterfaces(AdvisedSupport a, AdvisedSupport b * Check equality of the advisors behind the given AdvisedSupport objects. */ public static boolean equalsAdvisors(AdvisedSupport a, AdvisedSupport b) { - return Arrays.equals(a.getAdvisors(), b.getAdvisors()); + return a.getAdvisorCount() == b.getAdvisorCount() && Arrays.equals(a.getAdvisors(), b.getAdvisors()); } @@ -224,8 +224,8 @@ static Object[] adaptArgumentsIfNecessary(Method method, @Nullable Object[] argu return new Object[0]; } if (method.isVarArgs()) { - Class[] paramTypes = method.getParameterTypes(); - if (paramTypes.length == arguments.length) { + if (method.getParameterCount() == arguments.length) { + Class[] paramTypes = method.getParameterTypes(); int varargIndex = paramTypes.length - 1; Class varargType = paramTypes[varargIndex]; if (varargType.isArray()) { diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java b/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java index 1e899671aaaf..e2b822816dbf 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java @@ -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,7 +21,6 @@ import java.lang.reflect.Modifier; import java.lang.reflect.UndeclaredThrowableException; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -49,10 +48,12 @@ import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import org.springframework.cglib.proxy.NoOp; +import org.springframework.core.KotlinDetector; import org.springframework.core.SmartClassLoader; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; @@ -125,7 +126,7 @@ class CglibAopProxy implements AopProxy, Serializable { */ public CglibAopProxy(AdvisedSupport config) throws AopConfigException { Assert.notNull(config, "AdvisedSupport must not be null"); - if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) { + if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) { throw new AopConfigException("No advisors and no TargetSource specified"); } this.advised = config; @@ -258,15 +259,17 @@ private void doValidateClass(Class proxySuperClass, @Nullable ClassLoader pro int mod = method.getModifiers(); if (!Modifier.isStatic(mod) && !Modifier.isPrivate(mod)) { if (Modifier.isFinal(mod)) { - if (implementsInterface(method, ifcs)) { + if (logger.isInfoEnabled() && implementsInterface(method, ifcs)) { logger.info("Unable to proxy interface-implementing method [" + method + "] because " + "it is marked as final: Consider using interface-based JDK proxies instead!"); } - logger.debug("Final method [" + method + "] cannot get proxied via CGLIB: " + - "Calls to this method will NOT be routed to the target instance and " + - "might lead to NPEs against uninitialized fields in the proxy instance."); + if (logger.isDebugEnabled()) { + logger.debug("Final method [" + method + "] cannot get proxied via CGLIB: " + + "Calls to this method will NOT be routed to the target instance and " + + "might lead to NPEs against uninitialized fields in the proxy instance."); + } } - else if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod) && + else if (logger.isDebugEnabled() && !Modifier.isPublic(mod) && !Modifier.isProtected(mod) && proxyClassLoader != null && proxySuperClass.getClassLoader() != proxyClassLoader) { logger.debug("Method [" + method + "] is package-visible across different ClassLoaders " + "and cannot get proxied via CGLIB: Declare this method as public or protected " + @@ -323,7 +326,7 @@ private Callback[] getCallbacks(Class rootClass) throws Exception { if (isStatic && isFrozen) { Method[] methods = rootClass.getMethods(); Callback[] fixedCallbacks = new Callback[methods.length]; - this.fixedInterceptorMap = new HashMap<>(methods.length); + this.fixedInterceptorMap = CollectionUtils.newHashMap(methods.length); // TODO: small memory optimization here (can skip creation for methods with no advice) for (int x = 0; x < methods.length; x++) { @@ -365,7 +368,7 @@ public int hashCode() { */ private static boolean implementsInterface(Method method, Set> ifcs) { for (Class ifc : ifcs) { - if (ClassUtils.hasMethod(ifc, method.getName(), method.getParameterTypes())) { + if (ClassUtils.hasMethod(ifc, method)) { return true; } } @@ -526,7 +529,7 @@ public Object intercept(Object proxy, Method method, Object[] args, MethodProxy private static class StaticDispatcher implements Dispatcher, Serializable { @Nullable - private Object target; + private final Object target; public StaticDispatcher(@Nullable Object target) { this.target = target; @@ -552,7 +555,7 @@ public AdvisedDispatcher(AdvisedSupport advised) { } @Override - public Object loadObject() throws Exception { + public Object loadObject() { return this.advised; } } @@ -750,10 +753,17 @@ public Object proceed() throws Throwable { throw ex; } catch (Exception ex) { - if (ReflectionUtils.declaresException(getMethod(), ex.getClass())) { + if (ReflectionUtils.declaresException(getMethod(), ex.getClass()) || + KotlinDetector.isKotlinType(getMethod().getDeclaringClass())) { + // Propagate original exception if declared on the target method + // (with callers expecting it). Always propagate it for Kotlin code + // since checked exceptions do not have to be explicitly declared there. throw ex; } else { + // Checked exception thrown in the interceptor but not declared on the + // target method signature -> apply an UndeclaredThrowableException, + // aligned with standard JDK dynamic proxy behavior. throw new UndeclaredThrowableException(ex); } } @@ -872,15 +882,14 @@ public int accept(Method method) { } return AOP_PROXY; } - Method key = method; // Check to see if we have fixed interceptor to serve this method. // Else use the AOP_PROXY. - if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) { + if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(method)) { if (logger.isTraceEnabled()) { logger.trace("Method has advice and optimizations are enabled: " + method); } // We know that we are optimizing so we can use the FixedStaticChainInterceptors. - int index = this.fixedInterceptorMap.get(key); + int index = this.fixedInterceptorMap.get(method); return (index + this.fixedInterceptorOffset); } else { @@ -941,11 +950,11 @@ public boolean equals(@Nullable Object other) { } // Advice instance identity is unimportant to the proxy class: // All that matters is type and ordering. - Advisor[] thisAdvisors = this.advised.getAdvisors(); - Advisor[] thatAdvisors = otherAdvised.getAdvisors(); - if (thisAdvisors.length != thatAdvisors.length) { + if (this.advised.getAdvisorCount() != otherAdvised.getAdvisorCount()) { return false; } + Advisor[] thisAdvisors = this.advised.getAdvisors(); + Advisor[] thatAdvisors = otherAdvised.getAdvisors(); for (int i = 0; i < thisAdvisors.length; i++) { Advisor thisAdvisor = thisAdvisors[i]; Advisor thatAdvisor = thatAdvisors[i]; @@ -959,11 +968,11 @@ public boolean equals(@Nullable Object other) { return true; } - private boolean equalsAdviceClasses(Advisor a, Advisor b) { + private static boolean equalsAdviceClasses(Advisor a, Advisor b) { return (a.getAdvice().getClass() == b.getAdvice().getClass()); } - private boolean equalsPointcuts(Advisor a, Advisor b) { + private static boolean equalsPointcuts(Advisor a, Advisor b) { // If only one of the advisor (but not both) is PointcutAdvisor, then it is a mismatch. // Takes care of the situations where an IntroductionAdvisor is used (see SPR-3959). return (!(a instanceof PointcutAdvisor) || diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java index d304397b568d..3e8c838704c4 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import java.lang.reflect.Proxy; import org.springframework.aop.SpringProxy; +import org.springframework.core.NativeDetector; /** * Default {@link AopProxyFactory} implementation, creating either a CGLIB proxy @@ -38,6 +39,7 @@ * * @author Rod Johnson * @author Juergen Hoeller + * @author Sebastien Deleuze * @since 12.03.2004 * @see AdvisedSupport#setOptimize * @see AdvisedSupport#setProxyTargetClass @@ -46,9 +48,11 @@ @SuppressWarnings("serial") public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { + @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { - if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { + if (!NativeDetector.inNativeImage() && + (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) { Class targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java b/spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java index 43223de5a2bd..fcfaf93dd832 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java @@ -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. @@ -57,6 +57,7 @@ * @author Juergen Hoeller * @author Rob Harrop * @author Dave Syer + * @author Sergey Tsypanov * @see java.lang.reflect.Proxy * @see AdvisedSupport * @see ProxyFactory @@ -82,6 +83,8 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa /** Config used to configure this proxy. */ private final AdvisedSupport advised; + private final Class[] proxiedInterfaces; + /** * Is the {@link #equals} method defined on the proxied interfaces? */ @@ -101,10 +104,12 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa */ public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException { Assert.notNull(config, "AdvisedSupport must not be null"); - if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) { + if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) { throw new AopConfigException("No advisors and no TargetSource specified"); } this.advised = config; + this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); + findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces); } @@ -118,9 +123,7 @@ public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } - Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); - findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); - return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); + return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this); } /** diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyCreatorSupport.java b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyCreatorSupport.java index 1295af52b346..9fa04a33e92f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyCreatorSupport.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyCreatorSupport.java @@ -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. @@ -16,7 +16,7 @@ package org.springframework.aop.framework; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import org.springframework.util.Assert; @@ -34,7 +34,7 @@ public class ProxyCreatorSupport extends AdvisedSupport { private AopProxyFactory aopProxyFactory; - private final List listeners = new LinkedList<>(); + private final List listeners = new ArrayList<>(); /** Set to true when the first AOP proxy has been created. */ private boolean active = false; diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java index c9c081c47497..6c9efc49f0d7 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java @@ -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,9 +21,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.aopalliance.aop.Advice; import org.aopalliance.intercept.Interceptor; @@ -342,11 +340,8 @@ private synchronized Object newPrototypeInstance() { // an independent instance of the configuration. // In this case, no proxy will have an instance of this object's configuration, // but will have an independent copy. - if (logger.isTraceEnabled()) { - logger.trace("Creating copy of prototype ProxyFactoryBean config: " + this); - } - ProxyCreatorSupport copy = new ProxyCreatorSupport(getAopProxyFactory()); + // The copy needs a fresh advisor chain, and a fresh TargetSource. TargetSource targetSource = freshTargetSource(); copy.copyConfigurationFrom(this, targetSource, freshAdvisorChain()); @@ -359,9 +354,6 @@ private synchronized Object newPrototypeInstance() { } copy.setFrozen(this.freezeProxy); - if (logger.isTraceEnabled()) { - logger.trace("Using ProxyCreatorSupport copy: " + copy); - } return getProxy(copy.createAopProxy()); } @@ -447,16 +439,12 @@ private synchronized void initializeAdvisorChain() throws AopConfigException, Be // Materialize interceptor chain from bean names. for (String name : this.interceptorNames) { - if (logger.isTraceEnabled()) { - logger.trace("Configuring advisor or advice '" + name + "'"); - } - if (name.endsWith(GLOBAL_SUFFIX)) { if (!(this.beanFactory instanceof ListableBeanFactory)) { throw new AopConfigException( "Can only use global advisors or interceptors with a ListableBeanFactory"); } - addGlobalAdvisor((ListableBeanFactory) this.beanFactory, + addGlobalAdvisors((ListableBeanFactory) this.beanFactory, name.substring(0, name.length() - GLOBAL_SUFFIX.length())); } @@ -473,7 +461,7 @@ private synchronized void initializeAdvisorChain() throws AopConfigException, Be // Avoid unnecessary creation of prototype bean just for advisor chain initialization. advice = new PrototypePlaceholderAdvisor(name); } - addAdvisorOnChainCreation(advice, name); + addAdvisorOnChainCreation(advice); } } } @@ -496,11 +484,10 @@ private List freshAdvisorChain() { if (logger.isDebugEnabled()) { logger.debug("Refreshing bean named '" + pa.getBeanName() + "'"); } - // Replace the placeholder with a fresh prototype instance resulting - // from a getBean() lookup + // Replace the placeholder with a fresh prototype instance resulting from a getBean lookup if (this.beanFactory == null) { - throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " + - "- cannot resolve prototype advisor '" + pa.getBeanName() + "'"); + throw new IllegalStateException("No BeanFactory available anymore (probably due to " + + "serialization) - cannot resolve prototype advisor '" + pa.getBeanName() + "'"); } Object bean = this.beanFactory.getBean(pa.getBeanName()); Advisor refreshedAdvisor = namedBeanToAdvisor(bean); @@ -517,28 +504,26 @@ private List freshAdvisorChain() { /** * Add all global interceptors and pointcuts. */ - private void addGlobalAdvisor(ListableBeanFactory beanFactory, String prefix) { + private void addGlobalAdvisors(ListableBeanFactory beanFactory, String prefix) { String[] globalAdvisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Advisor.class); String[] globalInterceptorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Interceptor.class); - List beans = new ArrayList<>(globalAdvisorNames.length + globalInterceptorNames.length); - Map names = new HashMap<>(beans.size()); - for (String name : globalAdvisorNames) { - Object bean = beanFactory.getBean(name); - beans.add(bean); - names.put(bean, name); - } - for (String name : globalInterceptorNames) { - Object bean = beanFactory.getBean(name); - beans.add(bean); - names.put(bean, name); - } - AnnotationAwareOrderComparator.sort(beans); - for (Object bean : beans) { - String name = names.get(bean); - if (name.startsWith(prefix)) { - addAdvisorOnChainCreation(bean, name); + if (globalAdvisorNames.length > 0 || globalInterceptorNames.length > 0) { + List beans = new ArrayList<>(globalAdvisorNames.length + globalInterceptorNames.length); + for (String name : globalAdvisorNames) { + if (name.startsWith(prefix)) { + beans.add(beanFactory.getBean(name)); + } + } + for (String name : globalInterceptorNames) { + if (name.startsWith(prefix)) { + beans.add(beanFactory.getBean(name)); + } + } + AnnotationAwareOrderComparator.sort(beans); + for (Object bean : beans) { + addAdvisorOnChainCreation(bean); } } } @@ -549,17 +534,11 @@ private void addGlobalAdvisor(ListableBeanFactory beanFactory, String prefix) { * Because of these three possibilities, we can't type the signature * more strongly. * @param next advice, advisor or target object - * @param name bean name from which we obtained this object in our owning - * bean factory */ - private void addAdvisorOnChainCreation(Object next, String name) { + private void addAdvisorOnChainCreation(Object next) { // We need to convert to an Advisor if necessary so that our source reference // matches what we find from superclass interceptors. - Advisor advisor = namedBeanToAdvisor(next); - if (logger.isTraceEnabled()) { - logger.trace("Adding advisor with name '" + name + "'"); - } - addAdvisor(advisor); + addAdvisor(namedBeanToAdvisor(next)); } /** @@ -570,9 +549,7 @@ private void addAdvisorOnChainCreation(Object next, String name) { */ private TargetSource freshTargetSource() { if (this.targetName == null) { - if (logger.isTraceEnabled()) { - logger.trace("Not refreshing target: Bean name not specified in 'interceptorNames'."); - } + // Not refreshing target: bean name not specified in 'interceptorNames' return this.targetSource; } else { @@ -600,8 +577,8 @@ private Advisor namedBeanToAdvisor(Object next) { // We expected this to be an Advisor or Advice, // but it wasn't. This is a configuration error. throw new AopConfigException("Unknown advisor type " + next.getClass() + - "; Can only include Advisor or Advice type beans in interceptorNames chain except for last entry," + - "which may also be target or TargetSource", ex); + "; can only include Advisor or Advice type beans in interceptorNames chain " + + "except for last entry which may also be target instance or TargetSource", ex); } } @@ -612,7 +589,7 @@ private Advisor namedBeanToAdvisor(Object next) { protected void adviceChanged() { super.adviceChanged(); if (this.singleton) { - logger.debug("Advice has changed; recaching singleton instance"); + logger.debug("Advice has changed; re-caching singleton instance"); synchronized (this) { this.singletonInstance = null; } diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ReflectiveMethodInvocation.java b/spring-aop/src/main/java/org/springframework/aop/framework/ReflectiveMethodInvocation.java index 0eef701a932f..1db34223cb41 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/ReflectiveMethodInvocation.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/ReflectiveMethodInvocation.java @@ -212,8 +212,7 @@ public MethodInvocation invocableClone() { Object[] cloneArguments = this.arguments; if (this.arguments.length > 0) { // Build an independent copy of the arguments array. - cloneArguments = new Object[this.arguments.length]; - System.arraycopy(this.arguments, 0, cloneArguments, 0, this.arguments.length); + cloneArguments = this.arguments.clone(); } return invocableClone(cloneArguments); } diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AfterReturningAdviceInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AfterReturningAdviceInterceptor.java index 3e58109be18b..4ce1c45c87b2 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AfterReturningAdviceInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AfterReturningAdviceInterceptor.java @@ -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. @@ -23,6 +23,7 @@ import org.springframework.aop.AfterAdvice; import org.springframework.aop.AfterReturningAdvice; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -51,6 +52,7 @@ public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) { @Override + @Nullable public Object invoke(MethodInvocation mi) throws Throwable { Object retVal = mi.proceed(); this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/MethodBeforeAdviceInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/MethodBeforeAdviceInterceptor.java index 420c6203fba3..09683e02576e 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/MethodBeforeAdviceInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/MethodBeforeAdviceInterceptor.java @@ -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. @@ -23,12 +23,13 @@ import org.springframework.aop.BeforeAdvice; import org.springframework.aop.MethodBeforeAdvice; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** - * Interceptor to wrap am {@link org.springframework.aop.MethodBeforeAdvice}. - * Used internally by the AOP framework; application developers should not need - * to use this class directly. + * Interceptor to wrap a {@link MethodBeforeAdvice}. + *

Used internally by the AOP framework; application developers should not + * need to use this class directly. * * @author Rod Johnson * @see AfterReturningAdviceInterceptor @@ -51,6 +52,7 @@ public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) { @Override + @Nullable public Object invoke(MethodInvocation mi) throws Throwable { this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); return mi.proceed(); diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java index 541e3a32110b..fcca4f0d3f2f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java @@ -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. @@ -107,6 +107,7 @@ public int getHandlerMethodCount() { @Override + @Nullable public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java index f169de2a7a03..4900c3d4657c 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java @@ -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,11 +36,13 @@ * also override the inherited {@link #shouldSkip} method to exclude certain * objects from auto-proxying. * - *

Advisors or advices requiring ordering should implement the + *

Advisors or advices requiring ordering should be annotated with + * {@link org.springframework.core.annotation.Order @Order} or implement the * {@link org.springframework.core.Ordered} interface. This class sorts - * Advisors by Ordered order value. Advisors that don't implement the - * Ordered interface will be considered as unordered; they will appear - * at the end of the advisor chain in undefined order. + * advisors using the {@link AnnotationAwareOrderComparator}. Advisors that are + * not annotated with {@code @Order} or don't implement the {@code Ordered} + * interface will be considered as unordered; they will appear at the end of the + * advisor chain in an undefined order. * * @author Rod Johnson * @author Juergen Hoeller diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java index 50a9734bdf4a..79c1fe20ca33 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,6 +46,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; +import org.springframework.core.SmartClassLoader; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -271,19 +272,9 @@ public Object postProcessBeforeInstantiation(Class beanClass, String beanName return null; } - @Override - public boolean postProcessAfterInstantiation(Object bean, String beanName) { - return true; - } - @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { - return pvs; - } - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) { - return bean; + return pvs; // skip postProcessPropertyValues } /** @@ -468,7 +459,12 @@ protected Object createProxy(Class beanClass, @Nullable String beanName, proxyFactory.setPreFiltered(true); } - return proxyFactory.getProxy(getProxyClassLoader()); + // Use original ClassLoader if bean class not locally loaded in overriding class loader + ClassLoader classLoader = getProxyClassLoader(); + if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) { + classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader(); + } + return proxyFactory.getProxy(classLoader); } /** @@ -513,7 +509,10 @@ protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] List allInterceptors = new ArrayList<>(); if (specificInterceptors != null) { - allInterceptors.addAll(Arrays.asList(specificInterceptors)); + if (specificInterceptors.length > 0) { + // specificInterceptors may equals PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS + allInterceptors.addAll(Arrays.asList(specificInterceptors)); + } if (commonInterceptors.length > 0) { if (this.applyCommonInterceptorsFirst) { allInterceptors.addAll(0, Arrays.asList(commonInterceptors)); diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanNameAutoProxyCreator.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanNameAutoProxyCreator.java index cc2b16101dca..8c49d4ff7d28 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanNameAutoProxyCreator.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanNameAutoProxyCreator.java @@ -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. @@ -37,6 +37,7 @@ * "interceptorNames" property. * * @author Juergen Hoeller + * @author Sam Brannen * @since 10.10.2003 * @see #setBeanNames * @see #isMatch @@ -46,6 +47,8 @@ @SuppressWarnings("serial") public class BeanNameAutoProxyCreator extends AbstractAutoProxyCreator { + private static final String[] NO_ALIASES = new String[0]; + @Nullable private List beanNames; @@ -72,40 +75,70 @@ public void setBeanNames(String... beanNames) { /** - * Identify as bean to proxy if the bean name is in the configured list of names. + * Delegate to {@link AbstractAutoProxyCreator#getCustomTargetSource(Class, String)} + * if the bean name matches one of the names in the configured list of supported + * names, returning {@code null} otherwise. + * @since 5.3 + * @see #setBeanNames(String...) + */ + @Override + protected TargetSource getCustomTargetSource(Class beanClass, String beanName) { + return (isSupportedBeanName(beanClass, beanName) ? + super.getCustomTargetSource(beanClass, beanName) : null); + } + + /** + * Identify as a bean to proxy if the bean name matches one of the names in + * the configured list of supported names. + * @see #setBeanNames(String...) */ @Override @Nullable protected Object[] getAdvicesAndAdvisorsForBean( Class beanClass, String beanName, @Nullable TargetSource targetSource) { + return (isSupportedBeanName(beanClass, beanName) ? + PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS : DO_NOT_PROXY); + } + + /** + * Determine if the bean name for the given bean class matches one of the names + * in the configured list of supported names. + * @param beanClass the class of the bean to advise + * @param beanName the name of the bean + * @return {@code true} if the given bean name is supported + * @see #setBeanNames(String...) + */ + private boolean isSupportedBeanName(Class beanClass, String beanName) { if (this.beanNames != null) { + boolean isFactoryBean = FactoryBean.class.isAssignableFrom(beanClass); for (String mappedName : this.beanNames) { - if (FactoryBean.class.isAssignableFrom(beanClass)) { + if (isFactoryBean) { if (!mappedName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { continue; } mappedName = mappedName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); } if (isMatch(beanName, mappedName)) { - return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS; + return true; } - BeanFactory beanFactory = getBeanFactory(); - if (beanFactory != null) { - String[] aliases = beanFactory.getAliases(beanName); - for (String alias : aliases) { - if (isMatch(alias, mappedName)) { - return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS; - } + } + + BeanFactory beanFactory = getBeanFactory(); + String[] aliases = (beanFactory != null ? beanFactory.getAliases(beanName) : NO_ALIASES); + for (String alias : aliases) { + for (String mappedName : this.beanNames) { + if (isMatch(alias, mappedName)) { + return true; } } } } - return DO_NOT_PROXY; + return false; } /** - * Return if the given bean name matches the mapped name. + * Determine if the given bean name matches the mapped name. *

The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, * as well as direct equality. Can be overridden in subclasses. * @param beanName the bean name to check diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/LazyInitTargetSourceCreator.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/LazyInitTargetSourceCreator.java index 5761068e8aa2..01aa62b7607c 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/LazyInitTargetSourceCreator.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/LazyInitTargetSourceCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 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. @@ -23,30 +23,32 @@ import org.springframework.lang.Nullable; /** - * TargetSourceCreator that enforces a LazyInitTargetSource for each bean - * that is defined as "lazy-init". This will lead to a proxy created for - * each of those beans, allowing to fetch a reference to such a bean - * without actually initializing the target bean instance. + * {@code TargetSourceCreator} that enforces a {@link LazyInitTargetSource} for + * each bean that is defined as "lazy-init". This will lead to a proxy created for + * each of those beans, allowing to fetch a reference to such a bean without + * actually initializing the target bean instance. * - *

To be registered as custom TargetSourceCreator for an auto-proxy creator, - * in combination with custom interceptors for specific beans or for the - * creation of lazy-init proxies only. For example, as autodetected + *

To be registered as custom {@code TargetSourceCreator} for an auto-proxy + * creator, in combination with custom interceptors for specific beans or for the + * creation of lazy-init proxies only. For example, as an autodetected * infrastructure bean in an XML application context definition: * *

  * <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
+ *   <property name="beanNames" value="*" /> <!-- apply to all beans -->
  *   <property name="customTargetSourceCreators">
  *     <list>
- *       <bean class="org.springframework.aop.framework.autoproxy.target.LazyInitTargetSourceCreator"/>
+ *       <bean class="org.springframework.aop.framework.autoproxy.target.LazyInitTargetSourceCreator" />
  *     </list>
  *   </property>
  * </bean>
  *
  * <bean id="myLazyInitBean" class="mypackage.MyBeanClass" lazy-init="true">
- *   ...
+ *   <!-- ... -->
  * </bean>
* * @author Juergen Hoeller + * @author Sam Brannen * @since 1.2 * @see org.springframework.beans.factory.config.BeanDefinition#isLazyInit * @see org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#setCustomTargetSourceCreators diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractMonitoringInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractMonitoringInterceptor.java index 494de786e5ac..536e6e3ade39 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractMonitoringInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractMonitoringInterceptor.java @@ -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. @@ -103,7 +103,8 @@ protected String createInvocationTraceName(MethodInvocation invocation) { if (this.logTargetClassInvocation && clazz.isInstance(invocation.getThis())) { clazz = invocation.getThis().getClass(); } - return getPrefix() + clazz.getName() + '.' + method.getName() + getSuffix(); + String className = clazz.getName(); + return getPrefix() + className + '.' + method.getName() + getSuffix(); } } diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractTraceInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractTraceInterceptor.java index dd02d2d8ef63..892cf5cacc05 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractTraceInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractTraceInterceptor.java @@ -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. @@ -25,6 +25,7 @@ import org.springframework.aop.support.AopUtils; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; /** * Base {@code MethodInterceptor} implementation for tracing. @@ -151,6 +152,7 @@ protected Log getLoggerForInvocation(MethodInvocation invocation) { } else { Object target = invocation.getThis(); + Assert.state(target != null, "Target must not be null"); return LogFactory.getLog(getClassForLogging(target)); } } diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/ConcurrencyThrottleInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/ConcurrencyThrottleInterceptor.java index 09e3fa800cd2..dd802ce813bd 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/ConcurrencyThrottleInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/ConcurrencyThrottleInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 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,7 @@ import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; +import org.springframework.lang.Nullable; import org.springframework.util.ConcurrencyThrottleSupport; /** @@ -48,6 +49,7 @@ public ConcurrencyThrottleInterceptor() { } @Override + @Nullable public Object invoke(MethodInvocation methodInvocation) throws Throwable { beforeAccess(); try { diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/CustomizableTraceInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/CustomizableTraceInterceptor.java index 48fc03aee10b..a46c85d0d13b 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/CustomizableTraceInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/CustomizableTraceInterceptor.java @@ -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. @@ -147,7 +147,7 @@ public class CustomizableTraceInterceptor extends AbstractTraceInterceptor { /** * The {@code Pattern} used to match placeholders. */ - private static final Pattern PATTERN = Pattern.compile("\\$\\[\\p{Alpha}+\\]"); + private static final Pattern PATTERN = Pattern.compile("\\$\\[\\p{Alpha}+]"); /** * The {@code Set} of allowed placeholders. @@ -296,6 +296,8 @@ protected String replacePlaceholders(String message, MethodInvocation methodInvo @Nullable Object returnValue, @Nullable Throwable throwable, long invocationTime) { Matcher matcher = PATTERN.matcher(message); + Object target = methodInvocation.getThis(); + Assert.state(target != null, "Target must not be null"); StringBuffer output = new StringBuffer(); while (matcher.find()) { @@ -304,11 +306,11 @@ protected String replacePlaceholders(String message, MethodInvocation methodInvo matcher.appendReplacement(output, Matcher.quoteReplacement(methodInvocation.getMethod().getName())); } else if (PLACEHOLDER_TARGET_CLASS_NAME.equals(match)) { - String className = getClassForLogging(methodInvocation.getThis()).getName(); + String className = getClassForLogging(target).getName(); matcher.appendReplacement(output, Matcher.quoteReplacement(className)); } else if (PLACEHOLDER_TARGET_CLASS_SHORT_NAME.equals(match)) { - String shortName = ClassUtils.getShortName(getClassForLogging(methodInvocation.getThis())); + String shortName = ClassUtils.getShortName(getClassForLogging(target)); matcher.appendReplacement(output, Matcher.quoteReplacement(shortName)); } else if (PLACEHOLDER_ARGUMENTS.equals(match)) { diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/DebugInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/DebugInterceptor.java index cc7733152031..06ea6102909e 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/DebugInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/DebugInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 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,6 +18,8 @@ import org.aopalliance.intercept.MethodInvocation; +import org.springframework.lang.Nullable; + /** * AOP Alliance {@code MethodInterceptor} that can be introduced in a chain * to display verbose information about intercepted invocations to the logger. @@ -56,6 +58,7 @@ public DebugInterceptor(boolean useDynamicLogger) { @Override + @Nullable public Object invoke(MethodInvocation invocation) throws Throwable { synchronized (this) { this.count++; diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/ExposeBeanNameAdvisors.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/ExposeBeanNameAdvisors.java index 351d6940116d..7dfb777dee39 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/ExposeBeanNameAdvisors.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/ExposeBeanNameAdvisors.java @@ -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. @@ -25,6 +25,7 @@ import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.aop.support.DelegatingIntroductionInterceptor; import org.springframework.beans.factory.NamedBean; +import org.springframework.lang.Nullable; /** * Convenient methods for creating advisors that may be used when autoproxying beans @@ -110,6 +111,7 @@ public ExposeBeanNameInterceptor(String beanName) { } @Override + @Nullable public Object invoke(MethodInvocation mi) throws Throwable { if (!(mi instanceof ProxyMethodInvocation)) { throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); @@ -134,6 +136,7 @@ public ExposeBeanNameIntroduction(String beanName) { } @Override + @Nullable public Object invoke(MethodInvocation mi) throws Throwable { if (!(mi instanceof ProxyMethodInvocation)) { throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/ExposeInvocationInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/ExposeInvocationInterceptor.java index 3b3f62fa7cc5..9822374da1a3 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/ExposeInvocationInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/ExposeInvocationInterceptor.java @@ -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. @@ -25,6 +25,7 @@ import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.core.NamedThreadLocal; import org.springframework.core.PriorityOrdered; +import org.springframework.lang.Nullable; /** * Interceptor that exposes the current {@link org.aopalliance.intercept.MethodInvocation} @@ -71,9 +72,11 @@ public static MethodInvocation currentInvocation() throws IllegalStateException MethodInvocation mi = invocation.get(); if (mi == null) { throw new IllegalStateException( - "No MethodInvocation found: Check that an AOP invocation is in progress, and that the " + + "No MethodInvocation found: Check that an AOP invocation is in progress and that the " + "ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that " + - "advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor!"); + "advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor! " + + "In addition, ExposeInvocationInterceptor and ExposeInvocationInterceptor.currentInvocation() " + + "must be invoked from the same thread."); } return mi; } @@ -86,6 +89,7 @@ private ExposeInvocationInterceptor() { } @Override + @Nullable public Object invoke(MethodInvocation mi) throws Throwable { MethodInvocation oldInvocation = invocation.get(); invocation.set(mi); diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleTraceInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleTraceInterceptor.java index 147408f24808..f53fd86ed937 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleTraceInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleTraceInterceptor.java @@ -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. @@ -19,6 +19,8 @@ import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; +import org.springframework.util.Assert; + /** * Simple AOP Alliance {@code MethodInterceptor} that can be introduced * in a chain to display verbose trace information about intercepted method @@ -73,8 +75,10 @@ protected Object invokeUnderTrace(MethodInvocation invocation, Log logger) throw * @return the description */ protected String getInvocationDescription(MethodInvocation invocation) { - return "method '" + invocation.getMethod().getName() + "' of class [" + - invocation.getThis().getClass().getName() + "]"; + Object target = invocation.getThis(); + Assert.state(target != null, "Target must not be null"); + String className = target.getClass().getName(); + return "method '" + invocation.getMethod().getName() + "' of class [" + className + "]"; } } diff --git a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java index cc3d00da8488..e1899cf8e5bc 100644 --- a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java +++ b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java @@ -124,8 +124,8 @@ public static String getOriginalBeanName(@Nullable String targetBeanName) { } /** - * Specify if the {@code beanName} is the name of a bean that references the target - * bean within a scoped proxy. + * Determine if the {@code beanName} is the name of a bean that references + * the target bean within a scoped proxy. * @since 4.1.4 */ public static boolean isScopedTarget(@Nullable String beanName) { diff --git a/spring-aop/src/main/java/org/springframework/aop/support/AbstractExpressionPointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/AbstractExpressionPointcut.java index b4218508edb0..5330f2c00d64 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/AbstractExpressionPointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/AbstractExpressionPointcut.java @@ -78,7 +78,7 @@ public void setExpression(@Nullable String expression) { * Called when a new pointcut expression is set. * The expression should be parsed at this point if possible. *

This implementation is empty. - * @param expression expression to set + * @param expression the expression to set * @throws IllegalArgumentException if the expression is invalid * @see #setExpression */ diff --git a/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java index 56b9f034eefe..6adeea047758 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java @@ -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. @@ -45,7 +45,7 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher @Nullable private final String methodName; - private final AtomicInteger evaluations = new AtomicInteger(0); + private final AtomicInteger evaluations = new AtomicInteger(); /** diff --git a/spring-aop/src/main/java/org/springframework/aop/support/DelegatePerTargetObjectIntroductionInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/support/DelegatePerTargetObjectIntroductionInterceptor.java index dd820c57fb7b..e55cf0b6d3bd 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/DelegatePerTargetObjectIntroductionInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/DelegatePerTargetObjectIntroductionInterceptor.java @@ -114,12 +114,13 @@ public Object invoke(MethodInvocation mi) throws Throwable { * that it is introduced into. This method is never called for * {@link MethodInvocation MethodInvocations} on the introduced interfaces. */ + @Nullable protected Object doProceed(MethodInvocation mi) throws Throwable { // If we get here, just pass the invocation on. return mi.proceed(); } - private Object getIntroductionDelegateFor(Object targetObject) { + private Object getIntroductionDelegateFor(@Nullable Object targetObject) { synchronized (this.delegateMap) { if (this.delegateMap.containsKey(targetObject)) { return this.delegateMap.get(targetObject); diff --git a/spring-aop/src/main/java/org/springframework/aop/support/DelegatingIntroductionInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/support/DelegatingIntroductionInterceptor.java index 3e97f8a83780..c7c5981fd38f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/DelegatingIntroductionInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/DelegatingIntroductionInterceptor.java @@ -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. @@ -131,6 +131,7 @@ public Object invoke(MethodInvocation mi) throws Throwable { * that it is introduced into. This method is never called for * {@link MethodInvocation MethodInvocations} on the introduced interfaces. */ + @Nullable protected Object doProceed(MethodInvocation mi) throws Throwable { // If we get here, just pass the invocation on. return mi.proceed(); diff --git a/spring-aop/src/main/java/org/springframework/aop/support/NameMatchMethodPointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/NameMatchMethodPointcut.java index faf1b0f4f3a0..d3d762465d51 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/NameMatchMethodPointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/NameMatchMethodPointcut.java @@ -66,7 +66,7 @@ public void setMappedNames(String... mappedNames) { * before a proxy is used. *

NB: This method does not work after the proxy is in * use, as advice chains will be cached. - * @param name name of the additional method that will match + * @param name the name of the additional method that will match * @return this pointcut to allow for multiple additions in one line */ public NameMatchMethodPointcut addMethodName(String name) { diff --git a/spring-aop/src/main/java/org/springframework/aop/support/NameMatchMethodPointcutAdvisor.java b/spring-aop/src/main/java/org/springframework/aop/support/NameMatchMethodPointcutAdvisor.java index 458b06de73c6..a7efd815dc5f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/NameMatchMethodPointcutAdvisor.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/NameMatchMethodPointcutAdvisor.java @@ -76,7 +76,7 @@ public void setMappedNames(String... mappedNames) { * Add another eligible method name, in addition to those already named. * Like the set methods, this method is for use when configuring proxies, * before a proxy is used. - * @param name name of the additional method that will match + * @param name the name of the additional method that will match * @return this pointcut to allow for multiple additions in one line * @see NameMatchMethodPointcut#addMethodName */ diff --git a/spring-aop/src/main/java/org/springframework/aop/target/AbstractPoolingTargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/AbstractPoolingTargetSource.java index 59017bfabeba..a5cfedbcbe0e 100644 --- a/spring-aop/src/main/java/org/springframework/aop/target/AbstractPoolingTargetSource.java +++ b/spring-aop/src/main/java/org/springframework/aop/target/AbstractPoolingTargetSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -116,7 +116,7 @@ public final void setBeanFactory(BeanFactory beanFactory) throws BeansException /** - * Return an IntroductionAdvisor that providing a mixin + * Return an IntroductionAdvisor that provides a mixin * exposing statistics about the pool maintained by this object. */ public DefaultIntroductionAdvisor getPoolingConfigMixin() { diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscovererTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscovererTests.java index 4a4b6242297b..bd37f5d3770c 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscovererTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscovererTests.java @@ -275,7 +275,7 @@ protected void assertException(Method method, String pointcut, String returning, private static String format(String[] names) { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); sb.append("("); for (int i = 0; i < names.length; i++) { sb.append(names[i]); diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJExpressionPointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJExpressionPointcutTests.java index 2ba65e3502b4..20f3106c6247 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJExpressionPointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJExpressionPointcutTests.java @@ -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. @@ -31,10 +31,10 @@ import org.springframework.aop.Pointcut; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.support.DefaultPointcutAdvisor; -import org.springframework.tests.sample.beans.IOther; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.tests.sample.beans.subpkg.DeepBean; +import org.springframework.beans.testfixture.beans.IOther; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.beans.testfixture.beans.subpkg.DeepBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -67,7 +67,7 @@ public void setUp() throws NoSuchMethodException { @Test public void testMatchExplicit() { - String expression = "execution(int org.springframework.tests.sample.beans.TestBean.getAge())"; + String expression = "execution(int org.springframework.beans.testfixture.beans.TestBean.getAge())"; Pointcut pointcut = getPointcut(expression); ClassFilter classFilter = pointcut.getClassFilter(); @@ -117,8 +117,8 @@ public void testTarget() throws SecurityException, NoSuchMethodException { * @param which this or target */ private void testThisOrTarget(String which) throws SecurityException, NoSuchMethodException { - String matchesTestBean = which + "(org.springframework.tests.sample.beans.TestBean)"; - String matchesIOther = which + "(org.springframework.tests.sample.beans.IOther)"; + String matchesTestBean = which + "(org.springframework.beans.testfixture.beans.TestBean)"; + String matchesIOther = which + "(org.springframework.beans.testfixture.beans.IOther)"; AspectJExpressionPointcut testBeanPc = new AspectJExpressionPointcut(); testBeanPc.setExpression(matchesTestBean); @@ -142,7 +142,7 @@ public void testWithinRootAndSubpackages() throws SecurityException, NoSuchMetho } private void testWithinPackage(boolean matchSubpackages) throws SecurityException, NoSuchMethodException { - String withinBeansPackage = "within(org.springframework.tests.sample.beans."; + String withinBeansPackage = "within(org.springframework.beans.testfixture.beans."; // Subpackages are matched by ** if (matchSubpackages) { withinBeansPackage += "."; @@ -187,7 +187,7 @@ public void testFriendlyErrorOnNoLocation3ArgMatching() { @Test public void testMatchWithArgs() throws Exception { - String expression = "execution(void org.springframework.tests.sample.beans.TestBean.setSomeNumber(Number)) && args(Double)"; + String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number)) && args(Double)"; Pointcut pointcut = getPointcut(expression); ClassFilter classFilter = pointcut.getClassFilter(); @@ -198,15 +198,15 @@ public void testMatchWithArgs() throws Exception { // not currently testable in a reliable fashion //assertDoesNotMatchStringClass(classFilter); - assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, new Double(12))).as("Should match with setSomeNumber with Double input").isTrue(); - assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, new Integer(11))).as("Should not match setSomeNumber with Integer input").isFalse(); + assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D)).as("Should match with setSomeNumber with Double input").isTrue(); + assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11)).as("Should not match setSomeNumber with Integer input").isFalse(); assertThat(methodMatcher.matches(getAge, TestBean.class)).as("Should not match getAge").isFalse(); assertThat(methodMatcher.isRuntime()).as("Should be a runtime match").isTrue(); } @Test public void testSimpleAdvice() { - String expression = "execution(int org.springframework.tests.sample.beans.TestBean.getAge())"; + String expression = "execution(int org.springframework.beans.testfixture.beans.TestBean.getAge())"; CallCountingInterceptor interceptor = new CallCountingInterceptor(); TestBean testBean = getAdvisedProxy(expression, interceptor); @@ -219,21 +219,21 @@ public void testSimpleAdvice() { @Test public void testDynamicMatchingProxy() { - String expression = "execution(void org.springframework.tests.sample.beans.TestBean.setSomeNumber(Number)) && args(Double)"; + String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number)) && args(Double)"; CallCountingInterceptor interceptor = new CallCountingInterceptor(); TestBean testBean = getAdvisedProxy(expression, interceptor); assertThat(interceptor.getCount()).as("Calls should be 0").isEqualTo(0); - testBean.setSomeNumber(new Double(30)); + testBean.setSomeNumber(30D); assertThat(interceptor.getCount()).as("Calls should be 1").isEqualTo(1); - testBean.setSomeNumber(new Integer(90)); + testBean.setSomeNumber(90); assertThat(interceptor.getCount()).as("Calls should be 1").isEqualTo(1); } @Test public void testInvalidExpression() { - String expression = "execution(void org.springframework.tests.sample.beans.TestBean.setSomeNumber(Number) && args(Double)"; + String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number) && args(Double)"; assertThatIllegalArgumentException().isThrownBy( getPointcut(expression)::getClassFilter); // call to getClassFilter forces resolution } @@ -264,7 +264,7 @@ private void assertMatchesTestBeanClass(ClassFilter classFilter) { @Test public void testWithUnsupportedPointcutPrimitive() { - String expression = "call(int org.springframework.tests.sample.beans.TestBean.getAge())"; + String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())"; assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class).isThrownBy(() -> getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution... .satisfies(ex -> assertThat(ex.getUnsupportedPrimitive()).isEqualTo(PointcutPrimitive.CALL)); diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/BeanNamePointcutMatchingTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/BeanNamePointcutMatchingTests.java index 37787c686464..3fd1b1e0c844 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/BeanNamePointcutMatchingTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/BeanNamePointcutMatchingTests.java @@ -18,7 +18,7 @@ import org.junit.jupiter.api.Test; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/MethodInvocationProceedingJoinPointTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/MethodInvocationProceedingJoinPointTests.java index 13c911c3343a..4591b74624aa 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/MethodInvocationProceedingJoinPointTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/MethodInvocationProceedingJoinPointTests.java @@ -33,8 +33,8 @@ import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.interceptor.ExposeInvocationInterceptor; import org.springframework.aop.support.AopUtils; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/TigerAspectJExpressionPointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/TigerAspectJExpressionPointcutTests.java index 5e9acc89d426..23d63fb1bea0 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/TigerAspectJExpressionPointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/TigerAspectJExpressionPointcutTests.java @@ -27,7 +27,7 @@ import test.annotation.transaction.Tx; import org.springframework.aop.framework.ProxyFactory; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; @@ -56,7 +56,7 @@ public void setup() throws NoSuchMethodException { @Test public void testMatchGenericArgument() { - String expression = "execution(* set*(java.util.List) )"; + String expression = "execution(* set*(java.util.List) )"; AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(); ajexp.setExpression(expression); diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/TrickyAspectJPointcutExpressionTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/TrickyAspectJPointcutExpressionTests.java index a69b4c76b2a5..7a6c7e93c7b1 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/TrickyAspectJPointcutExpressionTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/TrickyAspectJPointcutExpressionTests.java @@ -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. @@ -87,13 +87,12 @@ public void testManualProxyJavaWithStaticPointcutAndTwoClassLoaders() throws Exc testAdvice(new DefaultPointcutAdvisor(pointcut, logAdvice), logAdvice, new TestServiceImpl(), "TestServiceImpl"); // Then try again with a different class loader on the target... - SimpleThrowawayClassLoader loader = new SimpleThrowawayClassLoader(new TestServiceImpl().getClass().getClassLoader()); + SimpleThrowawayClassLoader loader = new SimpleThrowawayClassLoader(TestServiceImpl.class.getClassLoader()); // Make sure the interface is loaded from the parent class loader loader.excludeClass(TestService.class.getName()); loader.excludeClass(TestException.class.getName()); - TestService other = (TestService) loader.loadClass(TestServiceImpl.class.getName()).newInstance(); + TestService other = (TestService) loader.loadClass(TestServiceImpl.class.getName()).getDeclaredConstructor().newInstance(); testAdvice(new DefaultPointcutAdvisor(pointcut, logAdvice), logAdvice, other, "TestServiceImpl"); - } private void testAdvice(Advisor advisor, LogUserAdvice logAdvice, TestService target, String message) @@ -127,7 +126,6 @@ public static class SimpleThrowawayClassLoader extends OverridingClassLoader { public SimpleThrowawayClassLoader(ClassLoader parent) { super(parent); } - } diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/TypePatternClassFilterTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/TypePatternClassFilterTests.java index feaefe0e7f70..43373063622c 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/TypePatternClassFilterTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/TypePatternClassFilterTests.java @@ -20,11 +20,11 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.tests.sample.beans.CountingTestBean; -import org.springframework.tests.sample.beans.IOther; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.tests.sample.beans.subpkg.DeepBean; +import org.springframework.beans.testfixture.beans.CountingTestBean; +import org.springframework.beans.testfixture.beans.IOther; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.beans.testfixture.beans.subpkg.DeepBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -57,7 +57,7 @@ void invocationOfMatchesMethodBlowsUpWhenNoTypePatternHasBeenSet() throws Except @Test void validPatternMatching() { - TypePatternClassFilter tpcf = new TypePatternClassFilter("org.springframework.tests.sample.beans.*"); + TypePatternClassFilter tpcf = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*"); assertThat(tpcf.matches(TestBean.class)).as("Must match: in package").isTrue(); assertThat(tpcf.matches(ITestBean.class)).as("Must match: in package").isTrue(); @@ -70,7 +70,7 @@ void validPatternMatching() { @Test void subclassMatching() { - TypePatternClassFilter tpcf = new TypePatternClassFilter("org.springframework.tests.sample.beans.ITestBean+"); + TypePatternClassFilter tpcf = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.ITestBean+"); assertThat(tpcf.matches(TestBean.class)).as("Must match: in package").isTrue(); assertThat(tpcf.matches(ITestBean.class)).as("Must match: in package").isTrue(); @@ -98,8 +98,8 @@ void andOrNotReplacement() { @Test void testEquals() { - TypePatternClassFilter filter1 = new TypePatternClassFilter("org.springframework.tests.sample.beans.*"); - TypePatternClassFilter filter2 = new TypePatternClassFilter("org.springframework.tests.sample.beans.*"); + TypePatternClassFilter filter1 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*"); + TypePatternClassFilter filter2 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*"); TypePatternClassFilter filter3 = new TypePatternClassFilter("org.springframework.tests.*"); assertThat(filter1).isEqualTo(filter2); @@ -108,8 +108,8 @@ void testEquals() { @Test void testHashCode() { - TypePatternClassFilter filter1 = new TypePatternClassFilter("org.springframework.tests.sample.beans.*"); - TypePatternClassFilter filter2 = new TypePatternClassFilter("org.springframework.tests.sample.beans.*"); + TypePatternClassFilter filter1 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*"); + TypePatternClassFilter filter2 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*"); TypePatternClassFilter filter3 = new TypePatternClassFilter("org.springframework.tests.*"); assertThat(filter1.hashCode()).isEqualTo(filter2.hashCode()); @@ -118,11 +118,11 @@ void testHashCode() { @Test void testToString() { - TypePatternClassFilter filter1 = new TypePatternClassFilter("org.springframework.tests.sample.beans.*"); - TypePatternClassFilter filter2 = new TypePatternClassFilter("org.springframework.tests.sample.beans.*"); + TypePatternClassFilter filter1 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*"); + TypePatternClassFilter filter2 = new TypePatternClassFilter("org.springframework.beans.testfixture.beans.*"); assertThat(filter1.toString()) - .isEqualTo("org.springframework.aop.aspectj.TypePatternClassFilter: org.springframework.tests.sample.beans.*"); + .isEqualTo("org.springframework.aop.aspectj.TypePatternClassFilter: org.springframework.beans.testfixture.beans.*"); assertThat(filter1.toString()).isEqualTo(filter2.toString()); } diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java index 0790a58f8adb..612aa61af66a 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; import java.rmi.RemoteException; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import org.aspectj.lang.JoinPoint; @@ -50,11 +50,11 @@ import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.interceptor.ExposeInvocationInterceptor; import org.springframework.aop.support.AopUtils; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.OrderComparator; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; import org.springframework.util.ObjectUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -63,14 +63,16 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException; /** - * Abstract tests for AspectJAdvisorFactory. - * See subclasses for tests of concrete factories. + * Abstract tests for {@link AspectJAdvisorFactory} implementations. + * + *

See subclasses for tests of concrete factories. * * @author Rod Johnson * @author Chris Beams * @author Phillip Webb + * @author Sam Brannen */ -public abstract class AbstractAspectJAdvisorFactoryTests { +abstract class AbstractAspectJAdvisorFactoryTests { /** * To be overridden by concrete test subclasses. @@ -80,7 +82,7 @@ public abstract class AbstractAspectJAdvisorFactoryTests { @Test - public void testRejectsPerCflowAspect() { + void rejectsPerCflowAspect() { assertThatExceptionOfType(AopConfigException.class).isThrownBy(() -> getFixture().getAdvisors( new SingletonMetadataAwareAspectInstanceFactory(new PerCflowAspect(), "someBean"))) @@ -88,7 +90,7 @@ public void testRejectsPerCflowAspect() { } @Test - public void testRejectsPerCflowBelowAspect() { + void rejectsPerCflowBelowAspect() { assertThatExceptionOfType(AopConfigException.class).isThrownBy(() -> getFixture().getAdvisors( new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean"))) @@ -96,7 +98,7 @@ public void testRejectsPerCflowBelowAspect() { } @Test - public void testPerTargetAspect() throws SecurityException, NoSuchMethodException { + void perTargetAspect() throws SecurityException, NoSuchMethodException { TestBean target = new TestBean(); int realAge = 65; target.setAge(realAge); @@ -128,12 +130,12 @@ public void testPerTargetAspect() throws SecurityException, NoSuchMethodExceptio } @Test - public void testMultiplePerTargetAspects() throws SecurityException, NoSuchMethodException { + void multiplePerTargetAspects() throws SecurityException, NoSuchMethodException { TestBean target = new TestBean(); int realAge = 65; target.setAge(realAge); - List advisors = new LinkedList<>(); + List advisors = new ArrayList<>(); PerTargetAspect aspect1 = new PerTargetAspect(); aspect1.count = 100; aspect1.setOrder(10); @@ -156,12 +158,12 @@ public void testMultiplePerTargetAspects() throws SecurityException, NoSuchMetho } @Test - public void testMultiplePerTargetAspectsWithOrderAnnotation() throws SecurityException, NoSuchMethodException { + void multiplePerTargetAspectsWithOrderAnnotation() throws SecurityException, NoSuchMethodException { TestBean target = new TestBean(); int realAge = 65; target.setAge(realAge); - List advisors = new LinkedList<>(); + List advisors = new ArrayList<>(); PerTargetAspectWithOrderAnnotation10 aspect1 = new PerTargetAspectWithOrderAnnotation10(); aspect1.count = 100; advisors.addAll( @@ -182,7 +184,7 @@ public void testMultiplePerTargetAspectsWithOrderAnnotation() throws SecurityExc } @Test - public void testPerThisAspect() throws SecurityException, NoSuchMethodException { + void perThisAspect() throws SecurityException, NoSuchMethodException { TestBean target = new TestBean(); int realAge = 65; target.setAge(realAge); @@ -218,7 +220,7 @@ public void testPerThisAspect() throws SecurityException, NoSuchMethodException } @Test - public void testPerTypeWithinAspect() throws SecurityException, NoSuchMethodException { + void perTypeWithinAspect() throws SecurityException, NoSuchMethodException { TestBean target = new TestBean(); int realAge = 65; target.setAge(realAge); @@ -259,22 +261,22 @@ public void testPerTypeWithinAspect() throws SecurityException, NoSuchMethodExce } @Test - public void testNamedPointcutAspectWithFQN() { - testNamedPointcuts(new NamedPointcutAspectWithFQN()); + void namedPointcutAspectWithFQN() { + namedPointcuts(new NamedPointcutAspectWithFQN()); } @Test - public void testNamedPointcutAspectWithoutFQN() { - testNamedPointcuts(new NamedPointcutAspectWithoutFQN()); + void namedPointcutAspectWithoutFQN() { + namedPointcuts(new NamedPointcutAspectWithoutFQN()); } @Test - public void testNamedPointcutFromAspectLibrary() { - testNamedPointcuts(new NamedPointcutAspectFromLibrary()); + void namedPointcutFromAspectLibrary() { + namedPointcuts(new NamedPointcutAspectFromLibrary()); } @Test - public void testNamedPointcutFromAspectLibraryWithBinding() { + void namedPointcutFromAspectLibraryWithBinding() { TestBean target = new TestBean(); ITestBean itb = (ITestBean) createProxy(target, getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory( @@ -285,7 +287,7 @@ public void testNamedPointcutFromAspectLibraryWithBinding() { assertThat(target.getAge()).isEqualTo(20); } - private void testNamedPointcuts(Object aspectInstance) { + private void namedPointcuts(Object aspectInstance) { TestBean target = new TestBean(); int realAge = 65; target.setAge(realAge); @@ -297,7 +299,7 @@ private void testNamedPointcuts(Object aspectInstance) { } @Test - public void testBindingWithSingleArg() { + void bindingWithSingleArg() { TestBean target = new TestBean(); ITestBean itb = (ITestBean) createProxy(target, getFixture().getAdvisors( @@ -309,7 +311,7 @@ public void testBindingWithSingleArg() { } @Test - public void testBindingWithMultipleArgsDifferentlyOrdered() { + void bindingWithMultipleArgsDifferentlyOrdered() { ManyValuedArgs target = new ManyValuedArgs(); ManyValuedArgs mva = (ManyValuedArgs) createProxy(target, getFixture().getAdvisors( @@ -329,7 +331,7 @@ public void testBindingWithMultipleArgsDifferentlyOrdered() { * In this case the introduction will be made. */ @Test - public void testIntroductionOnTargetNotImplementingInterface() { + void introductionOnTargetNotImplementingInterface() { NotLockable notLockableTarget = new NotLockable(); assertThat(notLockableTarget instanceof Lockable).isFalse(); NotLockable notLockable1 = (NotLockable) createProxy(notLockableTarget, @@ -358,7 +360,7 @@ public void testIntroductionOnTargetNotImplementingInterface() { } @Test - public void testIntroductionAdvisorExcludedFromTargetImplementingInterface() { + void introductionAdvisorExcludedFromTargetImplementingInterface() { assertThat(AopUtils.findAdvisorsThatCanApply( getFixture().getAdvisors( new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")), @@ -368,7 +370,7 @@ public void testIntroductionAdvisorExcludedFromTargetImplementingInterface() { } @Test - public void testIntroductionOnTargetImplementingInterface() { + void introductionOnTargetImplementingInterface() { CannotBeUnlocked target = new CannotBeUnlocked(); Lockable proxy = (Lockable) createProxy(target, // Ensure that we exclude @@ -388,8 +390,8 @@ public void testIntroductionOnTargetImplementingInterface() { } @Test - public void testIntroductionOnTargetExcludedByTypePattern() { - LinkedList target = new LinkedList<>(); + void introductionOnTargetExcludedByTypePattern() { + ArrayList target = new ArrayList<>(); List proxy = (List) createProxy(target, AopUtils.findAdvisorsThatCanApply( getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")), @@ -400,7 +402,7 @@ public void testIntroductionOnTargetExcludedByTypePattern() { } @Test - public void testIntroductionBasedOnAnnotationMatch_SPR5307() { + void introductionBasedOnAnnotationMatch_SPR5307() { AnnotatedTarget target = new AnnotatedTargetImpl(); List advisors = getFixture().getAdvisors( new SingletonMetadataAwareAspectInstanceFactory(new MakeAnnotatedTypeModifiable(), "someBean")); @@ -414,7 +416,7 @@ public void testIntroductionBasedOnAnnotationMatch_SPR5307() { // TODO: Why does this test fail? It hasn't been run before, so it maybe never actually passed... @Test @Disabled - public void testIntroductionWithArgumentBinding() { + void introductionWithArgumentBinding() { TestBean target = new TestBean(); List advisors = getFixture().getAdvisors( @@ -448,11 +450,11 @@ public void testIntroductionWithArgumentBinding() { } @Test - public void testAspectMethodThrowsExceptionLegalOnSignature() { + void aspectMethodThrowsExceptionLegalOnSignature() { TestBean target = new TestBean(); UnsupportedOperationException expectedException = new UnsupportedOperationException(); List advisors = getFixture().getAdvisors( - new SingletonMetadataAwareAspectInstanceFactory(new ExceptionAspect(expectedException), "someBean")); + new SingletonMetadataAwareAspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean")); assertThat(advisors.size()).as("One advice method was found").isEqualTo(1); ITestBean itb = (ITestBean) createProxy(target, advisors, ITestBean.class); assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy( @@ -462,11 +464,11 @@ public void testAspectMethodThrowsExceptionLegalOnSignature() { // TODO document this behaviour. // Is it different AspectJ behaviour, at least for checked exceptions? @Test - public void testAspectMethodThrowsExceptionIllegalOnSignature() { + void aspectMethodThrowsExceptionIllegalOnSignature() { TestBean target = new TestBean(); RemoteException expectedException = new RemoteException(); List advisors = getFixture().getAdvisors( - new SingletonMetadataAwareAspectInstanceFactory(new ExceptionAspect(expectedException), "someBean")); + new SingletonMetadataAwareAspectInstanceFactory(new ExceptionThrowingAspect(expectedException), "someBean")); assertThat(advisors.size()).as("One advice method was found").isEqualTo(1); ITestBean itb = (ITestBean) createProxy(target, advisors, ITestBean.class); assertThatExceptionOfType(UndeclaredThrowableException.class).isThrownBy( @@ -491,7 +493,7 @@ protected Object createProxy(Object target, List advisors, Class... } @Test - public void testTwoAdvicesOnOneAspect() { + void twoAdvicesOnOneAspect() { TestBean target = new TestBean(); TwoAdviceAspect twoAdviceAspect = new TwoAdviceAspect(); List advisors = getFixture().getAdvisors( @@ -506,25 +508,23 @@ public void testTwoAdvicesOnOneAspect() { } @Test - public void testAfterAdviceTypes() throws Exception { - Echo target = new Echo(); - ExceptionHandling afterReturningAspect = new ExceptionHandling(); + void afterAdviceTypes() throws Exception { + InvocationTrackingAspect aspect = new InvocationTrackingAspect(); List advisors = getFixture().getAdvisors( - new SingletonMetadataAwareAspectInstanceFactory(afterReturningAspect, "someBean")); - Echo echo = (Echo) createProxy(target, advisors, Echo.class); - assertThat(afterReturningAspect.successCount).isEqualTo(0); - assertThat(echo.echo("")).isEqualTo(""); - assertThat(afterReturningAspect.successCount).isEqualTo(1); - assertThat(afterReturningAspect.failureCount).isEqualTo(0); - assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() -> - echo.echo(new FileNotFoundException())); - assertThat(afterReturningAspect.successCount).isEqualTo(1); - assertThat(afterReturningAspect.failureCount).isEqualTo(1); - assertThat(afterReturningAspect.afterCount).isEqualTo(afterReturningAspect.failureCount + afterReturningAspect.successCount); + new SingletonMetadataAwareAspectInstanceFactory(aspect, "exceptionHandlingAspect")); + Echo echo = (Echo) createProxy(new Echo(), advisors, Echo.class); + + assertThat(aspect.invocations).isEmpty(); + assertThat(echo.echo(42)).isEqualTo(42); + assertThat(aspect.invocations).containsExactly("around - start", "before", "after returning", "after", "around - end"); + + aspect.invocations.clear(); + assertThatExceptionOfType(FileNotFoundException.class).isThrownBy(() -> echo.echo(new FileNotFoundException())); + assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end"); } @Test - public void testFailureWithoutExplicitDeclarePrecedence() { + void failureWithoutExplicitDeclarePrecedence() { TestBean target = new TestBean(); MetadataAwareAspectInstanceFactory aspectInstanceFactory = new SingletonMetadataAwareAspectInstanceFactory( new NoDeclarePrecedenceShouldFail(), "someBean"); @@ -534,7 +534,7 @@ public void testFailureWithoutExplicitDeclarePrecedence() { } @Test - public void testDeclarePrecedenceNotSupported() { + void declarePrecedenceNotSupported() { TestBean target = new TestBean(); assertThatIllegalArgumentException().isThrownBy(() -> { MetadataAwareAspectInstanceFactory aspectInstanceFactory = new SingletonMetadataAwareAspectInstanceFactory( @@ -545,28 +545,28 @@ public void testDeclarePrecedenceNotSupported() { @Aspect("percflow(execution(* *(..)))") - public static class PerCflowAspect { + static class PerCflowAspect { } @Aspect("percflowbelow(execution(* *(..)))") - public static class PerCflowBelowAspect { + static class PerCflowBelowAspect { } @Aspect("pertarget(execution(* *.getSpouse()))") @Order(10) - public static class PerTargetAspectWithOrderAnnotation10 { + static class PerTargetAspectWithOrderAnnotation10 { - public int count; + int count; @Around("execution(int *.getAge())") - public int returnCountAsAge() { + int returnCountAsAge() { return count++; } @Before("execution(void *.set*(int))") - public void countSetter() { + void countSetter() { ++count; } } @@ -574,34 +574,34 @@ public void countSetter() { @Aspect("pertarget(execution(* *.getSpouse()))") @Order(5) - public static class PerTargetAspectWithOrderAnnotation5 { + static class PerTargetAspectWithOrderAnnotation5 { - public int count; + int count; @Around("execution(int *.getAge())") - public int returnCountAsAge() { + int returnCountAsAge() { return count++; } @Before("execution(void *.set*(int))") - public void countSetter() { + void countSetter() { ++count; } } - @Aspect("pertypewithin(org.springframework.tests.sample.beans.IOther+)") - public static class PerTypeWithinAspect { + @Aspect("pertypewithin(org.springframework.beans.testfixture.beans.IOther+)") + static class PerTypeWithinAspect { - public int count; + int count; @Around("execution(int *.getAge())") - public int returnCountAsAge() { + int returnCountAsAge() { return count++; } @Before("execution(void *.*(..))") - public void countAnythingVoid() { + void countAnythingVoid() { ++count; } } @@ -611,7 +611,7 @@ private class PerTypeWithinAspectInstanceFactory implements MetadataAwareAspectI private int count; - public int getInstantiationCount() { + int getInstantiationCount() { return this.count; } @@ -644,97 +644,96 @@ public int getOrder() { @Aspect - public static class NamedPointcutAspectWithFQN { + static class NamedPointcutAspectWithFQN { @SuppressWarnings("unused") private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean(); @Pointcut("execution(* getAge())") - public void getAge() { + void getAge() { } @Around("org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.NamedPointcutAspectWithFQN.getAge()") - public int changeReturnValue(ProceedingJoinPoint pjp) { + int changeReturnValue(ProceedingJoinPoint pjp) { return -1; } } @Aspect - public static class NamedPointcutAspectWithoutFQN { + static class NamedPointcutAspectWithoutFQN { @Pointcut("execution(* getAge())") - public void getAge() { + void getAge() { } @Around("getAge()") - public int changeReturnValue(ProceedingJoinPoint pjp) { + int changeReturnValue(ProceedingJoinPoint pjp) { return -1; } } @Aspect - public static class NamedPointcutAspectFromLibrary { + static class NamedPointcutAspectFromLibrary { @Around("org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.Library.propertyAccess()") - public int changeReturnType(ProceedingJoinPoint pjp) { + int changeReturnType(ProceedingJoinPoint pjp) { return -1; } @Around(value="org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.Library.integerArgOperation(x)", argNames="x") - public void doubleArg(ProceedingJoinPoint pjp, int x) throws Throwable { + void doubleArg(ProceedingJoinPoint pjp, int x) throws Throwable { pjp.proceed(new Object[] {x*2}); } } @Aspect - public static class Library { + static class Library { @Pointcut("execution(!void get*())") - public void propertyAccess() {} + void propertyAccess() {} @Pointcut("execution(* *(..)) && args(i)") - public void integerArgOperation(int i) {} - + void integerArgOperation(int i) {} } @Aspect - public static class NamedPointcutAspectFromLibraryWithBinding { + static class NamedPointcutAspectFromLibraryWithBinding { @Around(value="org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.Library.integerArgOperation(x)", argNames="x") - public void doubleArg(ProceedingJoinPoint pjp, int x) throws Throwable { + void doubleArg(ProceedingJoinPoint pjp, int x) throws Throwable { pjp.proceed(new Object[] {x*2}); } } @Aspect - public static class BindingAspectWithSingleArg { + static class BindingAspectWithSingleArg { @Pointcut(value="args(a)", argNames="a") - public void setAge(int a) {} + void setAge(int a) {} @Around(value="setAge(age)",argNames="age") // @ArgNames({"age"}) // AMC needs more work here? ignoring pjp arg... ok?? // argNames should be supported in Around as it is in Pointcut - public void changeReturnType(ProceedingJoinPoint pjp, int age) throws Throwable { + void changeReturnType(ProceedingJoinPoint pjp, int age) throws Throwable { pjp.proceed(new Object[] {age*2}); } } @Aspect - public static class ManyValuedArgs { + static class ManyValuedArgs { - public String mungeArgs(String a, int b, int c, String d, StringBuffer e) { + String mungeArgs(String a, int b, int c, String d, StringBuffer e) { return a + b + c + d + e; } @Around(value="execution(String mungeArgs(..)) && args(a, b, c, d, e)", argNames="b,c,d,e,a") - public String reverseAdvice(ProceedingJoinPoint pjp, int b, int c, String d, StringBuffer e, String a) throws Throwable { + String reverseAdvice(ProceedingJoinPoint pjp, int b, int c, String d, StringBuffer e, String a) throws Throwable { assertThat(pjp.proceed()).isEqualTo(a + b+ c+ d+ e); return a + b + c + d + e; } @@ -742,24 +741,24 @@ public String reverseAdvice(ProceedingJoinPoint pjp, int b, int c, String d, Str @Aspect - public static class ExceptionAspect { + static class ExceptionThrowingAspect { private final Exception ex; - public ExceptionAspect(Exception ex) { + ExceptionThrowingAspect(Exception ex) { this.ex = ex; } @Before("execution(* getAge())") - public void throwException() throws Exception { + void throwException() throws Exception { throw ex; } } - public static class Echo { + static class Echo { - public Object echo(Object o) throws Exception { + Object echo(Object o) throws Exception { if (o instanceof Exception) { throw (Exception) o; } @@ -769,45 +768,62 @@ public Object echo(Object o) throws Exception { @Aspect - public static class ExceptionHandling { + private static class InvocationTrackingAspect { + + List invocations = new ArrayList<>(); + - public int successCount; + @Pointcut("execution(* echo(*))") + void echo() { + } - public int failureCount; + @Around("echo()") + Object around(ProceedingJoinPoint joinPoint) throws Throwable { + invocations.add("around - start"); + try { + return joinPoint.proceed(); + } + finally { + invocations.add("around - end"); + } + } - public int afterCount; + @Before("echo()") + void before() { + invocations.add("before"); + } - @AfterReturning("execution(* echo(*))") - public void succeeded() { - ++successCount; + @AfterReturning("echo()") + void afterReturning() { + invocations.add("after returning"); } - @AfterThrowing("execution(* echo(*))") - public void failed() { - ++failureCount; + @AfterThrowing("echo()") + void afterThrowing() { + invocations.add("after throwing"); } - @After("execution(* echo(*))") - public void invoked() { - ++afterCount; + @After("echo()") + void after() { + invocations.add("after"); } } @Aspect - public static class NoDeclarePrecedenceShouldFail { + static class NoDeclarePrecedenceShouldFail { @Pointcut("execution(int *.getAge())") - public void getAge() { + void getAge() { } @Before("getAge()") - public void blowUpButDoesntMatterBecauseAroundAdviceWontLetThisBeInvoked() { + void blowUpButDoesntMatterBecauseAroundAdviceWontLetThisBeInvoked() { throw new IllegalStateException(); } @Around("getAge()") - public int preventExecution(ProceedingJoinPoint pjp) { + int preventExecution(ProceedingJoinPoint pjp) { return 666; } } @@ -815,19 +831,19 @@ public int preventExecution(ProceedingJoinPoint pjp) { @Aspect @DeclarePrecedence("test..*") - public static class DeclarePrecedenceShouldSucceed { + static class DeclarePrecedenceShouldSucceed { @Pointcut("execution(int *.getAge())") - public void getAge() { + void getAge() { } @Before("getAge()") - public void blowUpButDoesntMatterBecauseAroundAdviceWontLetThisBeInvoked() { + void blowUpButDoesntMatterBecauseAroundAdviceWontLetThisBeInvoked() { throw new IllegalStateException(); } @Around("getAge()") - public int preventExecution(ProceedingJoinPoint pjp) { + int preventExecution(ProceedingJoinPoint pjp) { return 666; } } @@ -845,12 +861,12 @@ public int preventExecution(ProceedingJoinPoint pjp) { @Aspect abstract class AbstractMakeModifiable { - public interface MutableModifiable extends Modifiable { + interface MutableModifiable extends Modifiable { void markDirty(); } - public static class ModifiableImpl implements MutableModifiable { + static class ModifiableImpl implements MutableModifiable { private boolean modified; @@ -871,7 +887,7 @@ public void markDirty() { } @Before(value="execution(void set*(*)) && this(modifiable) && args(newValue)", argNames="modifiable,newValue") - public void recordModificationIfSetterArgumentDiffersFromOldValue( + void recordModificationIfSetterArgumentDiffersFromOldValue( JoinPoint jp, MutableModifiable mixin, Object newValue) { /* @@ -931,9 +947,9 @@ private Method getGetterFromSetter(Method setter) { @Aspect class MakeITestBeanModifiable extends AbstractMakeModifiable { - @DeclareParents(value = "org.springframework.tests.sample.beans.ITestBean+", + @DeclareParents(value = "org.springframework.beans.testfixture.beans.ITestBean+", defaultImpl=ModifiableImpl.class) - public static MutableModifiable mixin; + static MutableModifiable mixin; } @@ -948,7 +964,7 @@ class MakeAnnotatedTypeModifiable extends AbstractMakeModifiable { @DeclareParents(value = "(@org.springframework.aop.aspectj.annotation.Measured *)", defaultImpl = DefaultLockable.class) - public static Lockable mixin; + static Lockable mixin; } @@ -960,10 +976,10 @@ class MakeAnnotatedTypeModifiable extends AbstractMakeModifiable { class MakeLockable { @DeclareParents(value = "org.springframework..*", defaultImpl = DefaultLockable.class) - public static Lockable mixin; + static Lockable mixin; @Before(value="execution(void set*(*)) && this(mixin)", argNames="mixin") - public void checkNotLocked( Lockable mixin) { + void checkNotLocked( Lockable mixin) { // Can also obtain the mixin (this) this way //Lockable mixin = (Lockable) jp.getThis(); if (mixin.locked()) { @@ -1032,11 +1048,11 @@ class NotLockable { private int intValue; - public int getIntValue() { + int getIntValue() { return intValue; } - public void setIntValue(int intValue) { + void setIntValue(int intValue) { this.intValue = intValue; } @@ -1046,19 +1062,19 @@ public void setIntValue(int intValue) { @Aspect("perthis(execution(* *.getSpouse()))") class PerThisAspect { - public int count; + int count; // Just to check that this doesn't cause problems with introduction processing @SuppressWarnings("unused") private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean(); @Around("execution(int *.getAge())") - public int returnCountAsAge() { + int returnCountAsAge() { return count++; } @Before("execution(void *.set*(int))") - public void countSetter() { + void countSetter() { ++count; } diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/ArgumentBindingTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/ArgumentBindingTests.java index e0806be6f060..835f4a4f95c3 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/ArgumentBindingTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/ArgumentBindingTests.java @@ -27,8 +27,8 @@ import org.junit.jupiter.api.Test; import org.springframework.aop.aspectj.AspectJAdviceParameterNameDiscoverer; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectJPointcutAdvisorTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectJPointcutAdvisorTests.java index bbc816b3289a..806f30587bbe 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectJPointcutAdvisorTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectJPointcutAdvisorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,8 +22,9 @@ import org.springframework.aop.Pointcut; import org.springframework.aop.aspectj.AspectJExpressionPointcut; import org.springframework.aop.aspectj.AspectJExpressionPointcutTests; +import org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.ExceptionThrowingAspect; import org.springframework.aop.framework.AopConfigException; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -44,7 +45,7 @@ public void testSingleton() throws SecurityException, NoSuchMethodException { InstantiationModelAwarePointcutAdvisorImpl ajpa = new InstantiationModelAwarePointcutAdvisorImpl( ajexp, TestBean.class.getMethod("getAge"), af, - new SingletonMetadataAwareAspectInstanceFactory(new AbstractAspectJAdvisorFactoryTests.ExceptionAspect(null), "someBean"), + new SingletonMetadataAwareAspectInstanceFactory(new ExceptionThrowingAspect(null), "someBean"), 1, "someBean"); assertThat(ajpa.getAspectMetadata().getPerClausePointcut()).isSameAs(Pointcut.TRUE); diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectMetadataTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectMetadataTests.java index 55fa481ecea7..637baa2450a8 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectMetadataTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectMetadataTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ import org.springframework.aop.Pointcut; import org.springframework.aop.aspectj.AspectJExpressionPointcut; -import org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.ExceptionAspect; +import org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.ExceptionThrowingAspect; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -42,7 +42,7 @@ void notAnAspect() { @Test void singletonAspect() { - AspectMetadata am = new AspectMetadata(ExceptionAspect.class, "someBean"); + AspectMetadata am = new AspectMetadata(ExceptionThrowingAspect.class, "someBean"); assertThat(am.isPerThisOrPerTarget()).isFalse(); assertThat(am.getPerClausePointcut()).isSameAs(Pointcut.TRUE); assertThat(am.getAjType().getPerClause().getKind()).isEqualTo(PerClauseKind.SINGLETON); diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectProxyFactoryTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectProxyFactoryTests.java index 0c4d6ff1d8d1..a00a27bb6dec 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectProxyFactoryTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectProxyFactoryTests.java @@ -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. @@ -26,7 +26,7 @@ import org.junit.jupiter.api.Test; import test.aop.PerThisAspect; -import org.springframework.util.SerializationTestUtils; +import org.springframework.core.testfixture.io.SerializationTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -89,7 +89,7 @@ public void testSerializable() throws Exception { proxyFactory.addAspect(LoggingAspectOnVarargs.class); ITestBean proxy = proxyFactory.getProxy(); assertThat(proxy.doWithVarargs(MyEnum.A, MyOtherEnum.C)).isTrue(); - ITestBean tb = (ITestBean) SerializationTestUtils.serializeAndDeserialize(proxy); + ITestBean tb = SerializationTestUtils.serializeAndDeserialize(proxy); assertThat(tb.doWithVarargs(MyEnum.A, MyOtherEnum.C)).isTrue(); } @@ -108,15 +108,14 @@ public void testWithInstance() throws Exception { ITestBean proxy = proxyFactory.getProxy(); assertThat(proxy.getAge()).isEqualTo((target.getAge() * multiple)); - ITestBean serializedProxy = (ITestBean) SerializationTestUtils.serializeAndDeserialize(proxy); + ITestBean serializedProxy = SerializationTestUtils.serializeAndDeserialize(proxy); assertThat(serializedProxy.getAge()).isEqualTo((target.getAge() * multiple)); } @Test public void testWithNonSingletonAspectInstance() throws Exception { AspectJProxyFactory pf = new AspectJProxyFactory(); - assertThatIllegalArgumentException().isThrownBy(() -> - pf.addAspect(new PerThisAspect())); + assertThatIllegalArgumentException().isThrownBy(() -> pf.addAspect(new PerThisAspect())); } @Test // SPR-13328 diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactoryTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactoryTests.java index 3eac9fac5225..d649ea019642 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactoryTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 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,13 +17,14 @@ package org.springframework.aop.aspectj.annotation; /** - * Tests for ReflectiveAtAspectJAdvisorFactory. - * Tests are inherited: we only set the test fixture here. + * Tests for {@link ReflectiveAspectJAdvisorFactory}. + * + *

Tests are inherited: we only set the test fixture here. * * @author Rod Johnson * @since 2.0 */ -public class ReflectiveAspectJAdvisorFactoryTests extends AbstractAspectJAdvisorFactoryTests { +class ReflectiveAspectJAdvisorFactoryTests extends AbstractAspectJAdvisorFactoryTests { @Override protected AspectJAdvisorFactory getFixture() { diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/autoproxy/AspectJNamespaceHandlerTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/autoproxy/AspectJNamespaceHandlerTests.java index b2b1323b4fd9..a6ecf37304de 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/autoproxy/AspectJNamespaceHandlerTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/autoproxy/AspectJNamespaceHandlerTests.java @@ -29,7 +29,7 @@ import org.springframework.beans.factory.xml.ParserContext; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.beans.factory.xml.XmlReaderContext; -import org.springframework.tests.beans.CollectingReaderEventListener; +import org.springframework.beans.testfixture.beans.CollectingReaderEventListener; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-aop/src/test/java/org/springframework/aop/config/AopNamespaceHandlerEventTests.java b/spring-aop/src/test/java/org/springframework/aop/config/AopNamespaceHandlerEventTests.java index c31d5a396d04..d4c774424d04 100644 --- a/spring-aop/src/test/java/org/springframework/aop/config/AopNamespaceHandlerEventTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/config/AopNamespaceHandlerEventTests.java @@ -29,11 +29,11 @@ import org.springframework.beans.factory.parsing.CompositeComponentDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.beans.testfixture.beans.CollectingReaderEventListener; import org.springframework.core.io.Resource; -import org.springframework.tests.beans.CollectingReaderEventListener; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * @author Rob Harrop diff --git a/spring-aop/src/test/java/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests.java b/spring-aop/src/test/java/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests.java index 7b07a2f793ee..6a01b03ad7b2 100644 --- a/spring-aop/src/test/java/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests.java @@ -24,7 +24,7 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * @author Mark Fisher diff --git a/spring-aop/src/test/java/org/springframework/aop/config/TopLevelAopTagTests.java b/spring-aop/src/test/java/org/springframework/aop/config/TopLevelAopTagTests.java index 95732b92a1d7..f1ab7f273db9 100644 --- a/spring-aop/src/test/java/org/springframework/aop/config/TopLevelAopTagTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/config/TopLevelAopTagTests.java @@ -22,7 +22,7 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * Tests that the <aop:config/> element can be used as a top level element. diff --git a/spring-aop/src/test/java/org/springframework/aop/framework/AopProxyUtilsTests.java b/spring-aop/src/test/java/org/springframework/aop/framework/AopProxyUtilsTests.java index f2f9c624424e..c8475794f337 100644 --- a/spring-aop/src/test/java/org/springframework/aop/framework/AopProxyUtilsTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/framework/AopProxyUtilsTests.java @@ -23,8 +23,8 @@ import org.junit.jupiter.api.Test; import org.springframework.aop.SpringProxy; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; diff --git a/spring-aop/src/test/java/org/springframework/aop/framework/IntroductionBenchmarkTests.java b/spring-aop/src/test/java/org/springframework/aop/framework/IntroductionBenchmarkTests.java index 50092ca733b7..d3defccfb429 100644 --- a/spring-aop/src/test/java/org/springframework/aop/framework/IntroductionBenchmarkTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/framework/IntroductionBenchmarkTests.java @@ -19,8 +19,8 @@ import org.junit.jupiter.api.Test; import org.springframework.aop.support.DelegatingIntroductionInterceptor; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.util.StopWatch; /** diff --git a/spring-aop/src/test/java/org/springframework/aop/framework/MethodInvocationTests.java b/spring-aop/src/test/java/org/springframework/aop/framework/MethodInvocationTests.java index 9de8bd658c15..91091035aac7 100644 --- a/spring-aop/src/test/java/org/springframework/aop/framework/MethodInvocationTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/framework/MethodInvocationTests.java @@ -23,7 +23,7 @@ import org.aopalliance.intercept.MethodInterceptor; import org.junit.jupiter.api.Test; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-aop/src/test/java/org/springframework/aop/framework/PrototypeTargetTests.java b/spring-aop/src/test/java/org/springframework/aop/framework/PrototypeTargetTests.java index 6174c02a0c19..d58a9b594a16 100644 --- a/spring-aop/src/test/java/org/springframework/aop/framework/PrototypeTargetTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/framework/PrototypeTargetTests.java @@ -25,7 +25,7 @@ import org.springframework.core.io.Resource; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * @author Juergen Hoeller diff --git a/spring-aop/src/test/java/org/springframework/aop/framework/ProxyFactoryTests.java b/spring-aop/src/test/java/org/springframework/aop/framework/ProxyFactoryTests.java index 7372468f9c3b..2ae1d635116d 100644 --- a/spring-aop/src/test/java/org/springframework/aop/framework/ProxyFactoryTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/framework/ProxyFactoryTests.java @@ -33,15 +33,15 @@ import org.springframework.aop.support.AopUtils; import org.springframework.aop.support.DefaultIntroductionAdvisor; import org.springframework.aop.support.DefaultPointcutAdvisor; -import org.springframework.aop.support.DelegatingIntroductionInterceptor; +import org.springframework.aop.testfixture.advice.CountingBeforeAdvice; +import org.springframework.aop.testfixture.interceptor.NopInterceptor; +import org.springframework.aop.testfixture.interceptor.TimestampIntroductionInterceptor; +import org.springframework.beans.testfixture.beans.IOther; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.Order; -import org.springframework.tests.TimeStamped; -import org.springframework.tests.aop.advice.CountingBeforeAdvice; -import org.springframework.tests.aop.interceptor.NopInterceptor; -import org.springframework.tests.sample.beans.IOther; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.core.testfixture.TimeStamped; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -371,30 +371,6 @@ public void testInterceptorWithoutJoinpoint() { } - @SuppressWarnings("serial") - private static class TimestampIntroductionInterceptor extends DelegatingIntroductionInterceptor - implements TimeStamped { - - private long ts; - - public TimestampIntroductionInterceptor() { - } - - public TimestampIntroductionInterceptor(long ts) { - this.ts = ts; - } - - public void setTime(long ts) { - this.ts = ts; - } - - @Override - public long getTimeStamp() { - return ts; - } - } - - @Order(2) public static class A implements Runnable { diff --git a/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java b/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java index a836bcc00314..2c1060274e74 100644 --- a/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java @@ -17,16 +17,13 @@ package org.springframework.aop.framework.adapter; import java.io.FileNotFoundException; -import java.io.IOException; -import java.lang.reflect.Method; import java.rmi.ConnectException; import java.rmi.RemoteException; import org.aopalliance.intercept.MethodInvocation; import org.junit.jupiter.api.Test; -import org.springframework.aop.ThrowsAdvice; -import org.springframework.tests.aop.advice.MethodCounter; +import org.springframework.aop.testfixture.advice.MyThrowsHandler; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -128,23 +125,4 @@ public void afterThrowing(RemoteException ex) throws Throwable { assertThat(th.getCalls("remoteException")).isEqualTo(1); } - - @SuppressWarnings("serial") - static class MyThrowsHandler extends MethodCounter implements ThrowsAdvice { - - // Full method signature - public void afterThrowing(Method m, Object[] args, Object target, IOException ex) { - count("ioException"); - } - - public void afterThrowing(RemoteException ex) throws Throwable { - count("remoteException"); - } - - /** Not valid, wrong number of arguments */ - public void afterThrowing(Method m, Exception ex) throws Throwable { - throw new UnsupportedOperationException("Shouldn't be called"); - } - } - } diff --git a/spring-aop/src/test/java/org/springframework/aop/interceptor/ConcurrencyThrottleInterceptorTests.java b/spring-aop/src/test/java/org/springframework/aop/interceptor/ConcurrencyThrottleInterceptorTests.java index 9477df4ca017..9936573300d4 100644 --- a/spring-aop/src/test/java/org/springframework/aop/interceptor/ConcurrencyThrottleInterceptorTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/interceptor/ConcurrencyThrottleInterceptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,10 +22,10 @@ import org.springframework.aop.framework.Advised; import org.springframework.aop.framework.ProxyFactory; -import org.springframework.tests.sample.beans.DerivedTestBean; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.util.SerializationTestUtils; +import org.springframework.beans.testfixture.beans.DerivedTestBean; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.core.testfixture.io.SerializationTestUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -54,7 +54,7 @@ public void testSerializable() throws Exception { ITestBean proxy = (ITestBean) proxyFactory.getProxy(); proxy.getAge(); - ITestBean serializedProxy = (ITestBean) SerializationTestUtils.serializeAndDeserialize(proxy); + ITestBean serializedProxy = SerializationTestUtils.serializeAndDeserialize(proxy); Advised advised = (Advised) serializedProxy; ConcurrencyThrottleInterceptor serializedCti = (ConcurrencyThrottleInterceptor) advised.getAdvisors()[0].getAdvice(); diff --git a/spring-aop/src/test/java/org/springframework/aop/interceptor/CustomizableTraceInterceptorTests.java b/spring-aop/src/test/java/org/springframework/aop/interceptor/CustomizableTraceInterceptorTests.java index d5fcae4bd3c7..ebf44f27fcdd 100644 --- a/spring-aop/src/test/java/org/springframework/aop/interceptor/CustomizableTraceInterceptorTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/interceptor/CustomizableTraceInterceptorTests.java @@ -136,20 +136,20 @@ public void testSunnyDayPathLogsCorrectlyWithPrettyMuchAllPlaceholdersMatching() given(methodInvocation.getMethod()).willReturn(String.class.getMethod("toString", new Class[0])); given(methodInvocation.getThis()).willReturn(this); - given(methodInvocation.getArguments()).willReturn(new Object[]{"$ One \\$", new Long(2)}); + given(methodInvocation.getArguments()).willReturn(new Object[]{"$ One \\$", 2L}); given(methodInvocation.proceed()).willReturn("Hello!"); Log log = mock(Log.class); given(log.isTraceEnabled()).willReturn(true); CustomizableTraceInterceptor interceptor = new StubCustomizableTraceInterceptor(log); - interceptor.setEnterMessage(new StringBuffer() + interceptor.setEnterMessage(new StringBuilder() .append("Entering the '").append(CustomizableTraceInterceptor.PLACEHOLDER_METHOD_NAME) .append("' method of the [").append(CustomizableTraceInterceptor.PLACEHOLDER_TARGET_CLASS_NAME) .append("] class with the following args (").append(CustomizableTraceInterceptor.PLACEHOLDER_ARGUMENTS) .append(") and arg types (").append(CustomizableTraceInterceptor.PLACEHOLDER_ARGUMENT_TYPES) .append(").").toString()); - interceptor.setExitMessage(new StringBuffer() + interceptor.setExitMessage(new StringBuilder() .append("Exiting the '").append(CustomizableTraceInterceptor.PLACEHOLDER_METHOD_NAME) .append("' method of the [").append(CustomizableTraceInterceptor.PLACEHOLDER_TARGET_CLASS_SHORT_NAME) .append("] class with the following args (").append(CustomizableTraceInterceptor.PLACEHOLDER_ARGUMENTS) diff --git a/spring-aop/src/test/java/org/springframework/aop/interceptor/ExposeBeanNameAdvisorsTests.java b/spring-aop/src/test/java/org/springframework/aop/interceptor/ExposeBeanNameAdvisorsTests.java index ccb4d526313b..9bd43d1aef3f 100644 --- a/spring-aop/src/test/java/org/springframework/aop/interceptor/ExposeBeanNameAdvisorsTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/interceptor/ExposeBeanNameAdvisorsTests.java @@ -20,8 +20,8 @@ import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.factory.NamedBean; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-aop/src/test/java/org/springframework/aop/interceptor/ExposeInvocationInterceptorTests.java b/spring-aop/src/test/java/org/springframework/aop/interceptor/ExposeInvocationInterceptorTests.java index 76b682bf093e..331dc2b86809 100644 --- a/spring-aop/src/test/java/org/springframework/aop/interceptor/ExposeInvocationInterceptorTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/interceptor/ExposeInvocationInterceptorTests.java @@ -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,16 +16,14 @@ package org.springframework.aop.interceptor; -import org.aopalliance.intercept.MethodInvocation; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.ITestBean; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * Non-XML tests are in AbstractAopProxyTests @@ -48,33 +46,3 @@ public void testXmlConfig() { } } - - -abstract class ExposedInvocationTestBean extends TestBean { - - @Override - public String getName() { - MethodInvocation invocation = ExposeInvocationInterceptor.currentInvocation(); - assertions(invocation); - return super.getName(); - } - - @Override - public void absquatulate() { - MethodInvocation invocation = ExposeInvocationInterceptor.currentInvocation(); - assertions(invocation); - super.absquatulate(); - } - - protected abstract void assertions(MethodInvocation invocation); -} - - -class InvocationCheckExposedInvocationTestBean extends ExposedInvocationTestBean { - - @Override - protected void assertions(MethodInvocation invocation) { - assertThat(invocation.getThis() == this).isTrue(); - assertThat(ITestBean.class.isAssignableFrom(invocation.getMethod().getDeclaringClass())).as("Invocation should be on ITestBean: " + invocation.getMethod()).isTrue(); - } -} diff --git a/spring-aop/src/test/java/org/springframework/aop/interceptor/ExposedInvocationTestBean.java b/spring-aop/src/test/java/org/springframework/aop/interceptor/ExposedInvocationTestBean.java new file mode 100644 index 000000000000..95e8b7d2d39a --- /dev/null +++ b/spring-aop/src/test/java/org/springframework/aop/interceptor/ExposedInvocationTestBean.java @@ -0,0 +1,40 @@ +/* + * 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.aop.interceptor; + +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.beans.testfixture.beans.TestBean; + +abstract class ExposedInvocationTestBean extends TestBean { + + @Override + public String getName() { + MethodInvocation invocation = ExposeInvocationInterceptor.currentInvocation(); + assertions(invocation); + return super.getName(); + } + + @Override + public void absquatulate() { + MethodInvocation invocation = ExposeInvocationInterceptor.currentInvocation(); + assertions(invocation); + super.absquatulate(); + } + + protected abstract void assertions(MethodInvocation invocation); +} diff --git a/spring-aop/src/test/java/org/springframework/aop/interceptor/InvocationCheckExposedInvocationTestBean.java b/spring-aop/src/test/java/org/springframework/aop/interceptor/InvocationCheckExposedInvocationTestBean.java new file mode 100644 index 000000000000..05fdd00bf1fa --- /dev/null +++ b/spring-aop/src/test/java/org/springframework/aop/interceptor/InvocationCheckExposedInvocationTestBean.java @@ -0,0 +1,32 @@ +/* + * 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.aop.interceptor; + +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.beans.testfixture.beans.ITestBean; + +import static org.assertj.core.api.Assertions.assertThat; + +class InvocationCheckExposedInvocationTestBean extends ExposedInvocationTestBean { + + @Override + protected void assertions(MethodInvocation invocation) { + assertThat(invocation.getThis() == this).isTrue(); + assertThat(ITestBean.class.isAssignableFrom(invocation.getMethod().getDeclaringClass())).as("Invocation should be on ITestBean: " + invocation.getMethod()).isTrue(); + } +} diff --git a/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyAutowireTests.java b/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyAutowireTests.java index 42f32fa1eb02..a746532495b0 100644 --- a/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyAutowireTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyAutowireTests.java @@ -24,7 +24,7 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * @author Mark Fisher diff --git a/spring-aop/src/test/java/org/springframework/aop/support/AbstractRegexpMethodPointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/support/AbstractRegexpMethodPointcutTests.java index f13ebf47fea3..9970b496e2db 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/AbstractRegexpMethodPointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/AbstractRegexpMethodPointcutTests.java @@ -21,8 +21,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.util.SerializationTestUtils; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.core.testfixture.io.SerializationTestUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -49,7 +49,7 @@ public void testNoPatternSupplied() throws Exception { @Test public void testSerializationWithNoPatternSupplied() throws Exception { - rpc = (AbstractRegexpMethodPointcut) SerializationTestUtils.serializeAndDeserialize(rpc); + rpc = SerializationTestUtils.serializeAndDeserialize(rpc); noPatternSuppliedTests(rpc); } @@ -63,7 +63,7 @@ protected void noPatternSuppliedTests(AbstractRegexpMethodPointcut rpc) throws E public void testExactMatch() throws Exception { rpc.setPattern("java.lang.Object.hashCode"); exactMatchTests(rpc); - rpc = (AbstractRegexpMethodPointcut) SerializationTestUtils.serializeAndDeserialize(rpc); + rpc = SerializationTestUtils.serializeAndDeserialize(rpc); exactMatchTests(rpc); } diff --git a/spring-aop/src/test/java/org/springframework/aop/support/AopUtilsTests.java b/spring-aop/src/test/java/org/springframework/aop/support/AopUtilsTests.java index 63778647debc..dc5437e6cd67 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/AopUtilsTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/AopUtilsTests.java @@ -25,10 +25,10 @@ import org.springframework.aop.Pointcut; import org.springframework.aop.interceptor.ExposeInvocationInterceptor; import org.springframework.aop.target.EmptyTargetSource; +import org.springframework.aop.testfixture.interceptor.NopInterceptor; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.core.testfixture.io.SerializationTestUtils; import org.springframework.lang.Nullable; -import org.springframework.tests.aop.interceptor.NopInterceptor; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.util.SerializationTestUtils; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-aop/src/test/java/org/springframework/aop/support/ClassFiltersTests.java b/spring-aop/src/test/java/org/springframework/aop/support/ClassFiltersTests.java index 00ea9213e536..db83a9424df1 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/ClassFiltersTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/ClassFiltersTests.java @@ -19,9 +19,9 @@ import org.junit.jupiter.api.Test; import org.springframework.aop.ClassFilter; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.NestedRuntimeException; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-aop/src/test/java/org/springframework/aop/support/ClassUtilsTests.java b/spring-aop/src/test/java/org/springframework/aop/support/ClassUtilsTests.java index 39139bbbd02e..6eac64eb77b8 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/ClassUtilsTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/ClassUtilsTests.java @@ -18,7 +18,7 @@ import org.junit.jupiter.api.Test; import org.springframework.aop.framework.ProxyFactory; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.util.ClassUtils; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-aop/src/test/java/org/springframework/aop/support/ComposablePointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/support/ComposablePointcutTests.java index c4400b3371b9..87b408222c2b 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/ComposablePointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/ComposablePointcutTests.java @@ -23,9 +23,9 @@ import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.NestedRuntimeException; import org.springframework.lang.Nullable; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-aop/src/test/java/org/springframework/aop/support/ControlFlowPointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/support/ControlFlowPointcutTests.java index 72623dbf6559..f86c6e7ca9f1 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/ControlFlowPointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/ControlFlowPointcutTests.java @@ -20,9 +20,9 @@ import org.springframework.aop.Pointcut; import org.springframework.aop.framework.ProxyFactory; -import org.springframework.tests.aop.interceptor.NopInterceptor; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.aop.testfixture.interceptor.NopInterceptor; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-aop/src/test/java/org/springframework/aop/support/DelegatingIntroductionInterceptorTests.java b/spring-aop/src/test/java/org/springframework/aop/support/DelegatingIntroductionInterceptorTests.java index a165be41a406..3e6e08afc1ca 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/DelegatingIntroductionInterceptorTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/DelegatingIntroductionInterceptorTests.java @@ -24,15 +24,15 @@ import org.springframework.aop.IntroductionAdvisor; import org.springframework.aop.IntroductionInterceptor; import org.springframework.aop.framework.ProxyFactory; -import org.springframework.tests.TimeStamped; -import org.springframework.tests.aop.interceptor.SerializableNopInterceptor; -import org.springframework.tests.sample.beans.INestedTestBean; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.NestedTestBean; -import org.springframework.tests.sample.beans.Person; -import org.springframework.tests.sample.beans.SerializablePerson; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.util.SerializationTestUtils; +import org.springframework.aop.testfixture.interceptor.SerializableNopInterceptor; +import org.springframework.beans.testfixture.beans.INestedTestBean; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.NestedTestBean; +import org.springframework.beans.testfixture.beans.Person; +import org.springframework.beans.testfixture.beans.SerializablePerson; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.core.testfixture.TimeStamped; +import org.springframework.core.testfixture.io.SerializationTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -239,7 +239,7 @@ public void testSerializableDelegatingIntroductionInterceptorSerializable() thro assertThat(p.getName()).isEqualTo(name); assertThat(((TimeStamped) p).getTimeStamp()).isEqualTo(time); - Person p1 = (Person) SerializationTestUtils.serializeAndDeserialize(p); + Person p1 = SerializationTestUtils.serializeAndDeserialize(p); assertThat(p1.getName()).isEqualTo(name); assertThat(((TimeStamped) p1).getTimeStamp()).isEqualTo(time); } diff --git a/spring-aop/src/test/java/org/springframework/aop/support/MethodMatchersTests.java b/spring-aop/src/test/java/org/springframework/aop/support/MethodMatchersTests.java index f860ef72d4eb..55a2d7cabb57 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/MethodMatchersTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/MethodMatchersTests.java @@ -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,11 +21,11 @@ import org.junit.jupiter.api.Test; import org.springframework.aop.MethodMatcher; +import org.springframework.beans.testfixture.beans.IOther; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.core.testfixture.io.SerializationTestUtils; import org.springframework.lang.Nullable; -import org.springframework.tests.sample.beans.IOther; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.util.SerializationTestUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -83,12 +83,12 @@ public void testDynamicAndStaticMethodMatcherIntersection() throws Exception { MethodMatcher intersection = MethodMatchers.intersection(mm1, mm2); assertThat(intersection.isRuntime()).as("Intersection is a dynamic matcher").isTrue(); assertThat(intersection.matches(ITESTBEAN_SETAGE, TestBean.class)).as("2Matched setAge method").isTrue(); - assertThat(intersection.matches(ITESTBEAN_SETAGE, TestBean.class, new Integer(5))).as("3Matched setAge method").isTrue(); + assertThat(intersection.matches(ITESTBEAN_SETAGE, TestBean.class, 5)).as("3Matched setAge method").isTrue(); // Knock out dynamic part intersection = MethodMatchers.intersection(intersection, new TestDynamicMethodMatcherWhichDoesNotMatch()); assertThat(intersection.isRuntime()).as("Intersection is a dynamic matcher").isTrue(); assertThat(intersection.matches(ITESTBEAN_SETAGE, TestBean.class)).as("2Matched setAge method").isTrue(); - assertThat(intersection.matches(ITESTBEAN_SETAGE, TestBean.class, new Integer(5))).as("3 - not Matched setAge method").isFalse(); + assertThat(intersection.matches(ITESTBEAN_SETAGE, TestBean.class, 5)).as("3 - not Matched setAge method").isFalse(); } @Test diff --git a/spring-aop/src/test/java/org/springframework/aop/support/NameMatchMethodPointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/support/NameMatchMethodPointcutTests.java index 91517e6831c1..6c7928a83e6b 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/NameMatchMethodPointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/NameMatchMethodPointcutTests.java @@ -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,11 +21,11 @@ import org.springframework.aop.framework.Advised; import org.springframework.aop.framework.ProxyFactory; -import org.springframework.tests.aop.interceptor.NopInterceptor; -import org.springframework.tests.aop.interceptor.SerializableNopInterceptor; -import org.springframework.tests.sample.beans.Person; -import org.springframework.tests.sample.beans.SerializablePerson; -import org.springframework.util.SerializationTestUtils; +import org.springframework.aop.testfixture.interceptor.NopInterceptor; +import org.springframework.aop.testfixture.interceptor.SerializableNopInterceptor; +import org.springframework.beans.testfixture.beans.Person; +import org.springframework.beans.testfixture.beans.SerializablePerson; +import org.springframework.core.testfixture.io.SerializationTestUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -109,7 +109,7 @@ public void testSets() throws Throwable { public void testSerializable() throws Throwable { testSets(); // Count is now 2 - Person p2 = (Person) SerializationTestUtils.serializeAndDeserialize(proxied); + Person p2 = SerializationTestUtils.serializeAndDeserialize(proxied); NopInterceptor nop2 = (NopInterceptor) ((Advised) p2).getAdvisors()[0].getAdvice(); p2.getName(); assertThat(nop2.getCount()).isEqualTo(2); diff --git a/spring-aop/src/test/java/org/springframework/aop/support/PointcutsTests.java b/spring-aop/src/test/java/org/springframework/aop/support/PointcutsTests.java index 599354821a02..9a6f05e10171 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/PointcutsTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/PointcutsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,8 +22,8 @@ import org.springframework.aop.ClassFilter; import org.springframework.aop.Pointcut; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.lang.Nullable; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; @@ -121,20 +121,20 @@ public boolean matches(Method m, @Nullable Class targetClass) { @Test public void testTrue() { - assertThat(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))).isTrue(); + assertThat(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_SET_AGE, TestBean.class, 6)).isTrue(); assertThat(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_GET_AGE, TestBean.class)).isTrue(); assertThat(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_ABSQUATULATE, TestBean.class)).isTrue(); - assertThat(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))).isTrue(); + assertThat(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_SET_AGE, TestBean.class, 6)).isTrue(); assertThat(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_GET_AGE, TestBean.class)).isTrue(); assertThat(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_ABSQUATULATE, TestBean.class)).isTrue(); } @Test public void testMatches() { - assertThat(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))).isTrue(); + assertThat(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, 6)).isTrue(); assertThat(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_GET_AGE, TestBean.class)).isFalse(); assertThat(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_ABSQUATULATE, TestBean.class)).isFalse(); - assertThat(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))).isFalse(); + assertThat(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, 6)).isFalse(); assertThat(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_GET_AGE, TestBean.class)).isTrue(); assertThat(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_ABSQUATULATE, TestBean.class)).isFalse(); } @@ -145,7 +145,7 @@ public void testMatches() { @Test public void testUnionOfSettersAndGetters() { Pointcut union = Pointcuts.union(allClassGetterPointcut, allClassSetterPointcut); - assertThat(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))).isTrue(); + assertThat(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, 6)).isTrue(); assertThat(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)).isTrue(); assertThat(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class)).isFalse(); } @@ -153,7 +153,7 @@ public void testUnionOfSettersAndGetters() { @Test public void testUnionOfSpecificGetters() { Pointcut union = Pointcuts.union(allClassGetAgePointcut, allClassGetNamePointcut); - assertThat(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))).isFalse(); + assertThat(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, 6)).isFalse(); assertThat(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)).isTrue(); assertThat(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_NAME, TestBean.class)).isFalse(); assertThat(Pointcuts.matches(union, TEST_BEAN_GET_NAME, TestBean.class)).isTrue(); @@ -161,13 +161,13 @@ public void testUnionOfSpecificGetters() { // Union with all setters union = Pointcuts.union(union, allClassSetterPointcut); - assertThat(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))).isTrue(); + assertThat(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, 6)).isTrue(); assertThat(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)).isTrue(); assertThat(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_NAME, TestBean.class)).isFalse(); assertThat(Pointcuts.matches(union, TEST_BEAN_GET_NAME, TestBean.class)).isTrue(); assertThat(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class)).isFalse(); - assertThat(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))).isTrue(); + assertThat(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, 6)).isTrue(); } /** @@ -176,16 +176,16 @@ public void testUnionOfSpecificGetters() { */ @Test public void testUnionOfAllSettersAndSubclassSetters() { - assertThat(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))).isFalse(); - assertThat(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_SET_AGE, MyTestBean.class, new Integer(6))).isTrue(); + assertThat(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, 6)).isFalse(); + assertThat(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_SET_AGE, MyTestBean.class, 6)).isTrue(); assertThat(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_GET_AGE, TestBean.class)).isFalse(); Pointcut union = Pointcuts.union(myTestBeanSetterPointcut, allClassGetterPointcut); assertThat(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)).isTrue(); assertThat(Pointcuts.matches(union, TEST_BEAN_GET_AGE, MyTestBean.class)).isTrue(); // Still doesn't match superclass setter - assertThat(Pointcuts.matches(union, TEST_BEAN_SET_AGE, MyTestBean.class, new Integer(6))).isTrue(); - assertThat(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))).isFalse(); + assertThat(Pointcuts.matches(union, TEST_BEAN_SET_AGE, MyTestBean.class, 6)).isTrue(); + assertThat(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, 6)).isFalse(); } /** @@ -241,7 +241,7 @@ public void testIntersectionOfSpecificGettersAndSubclassGetters() { @Test public void testSimpleIntersection() { Pointcut intersection = Pointcuts.intersection(allClassGetterPointcut, allClassSetterPointcut); - assertThat(Pointcuts.matches(intersection, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))).isFalse(); + assertThat(Pointcuts.matches(intersection, TEST_BEAN_SET_AGE, TestBean.class, 6)).isFalse(); assertThat(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, TestBean.class)).isFalse(); assertThat(Pointcuts.matches(intersection, TEST_BEAN_ABSQUATULATE, TestBean.class)).isFalse(); } diff --git a/spring-aop/src/test/java/org/springframework/aop/support/RegexpMethodPointcutAdvisorIntegrationTests.java b/spring-aop/src/test/java/org/springframework/aop/support/RegexpMethodPointcutAdvisorIntegrationTests.java index da9699a305b3..c3b546c36049 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/RegexpMethodPointcutAdvisorIntegrationTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/RegexpMethodPointcutAdvisorIntegrationTests.java @@ -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. @@ -19,18 +19,18 @@ import org.junit.jupiter.api.Test; import org.springframework.aop.framework.Advised; +import org.springframework.aop.testfixture.interceptor.NopInterceptor; +import org.springframework.aop.testfixture.interceptor.SerializableNopInterceptor; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.Person; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.Resource; -import org.springframework.tests.aop.interceptor.NopInterceptor; -import org.springframework.tests.aop.interceptor.SerializableNopInterceptor; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.Person; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.util.SerializationTestUtils; +import org.springframework.core.testfixture.io.SerializationTestUtils; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * @author Rod Johnson @@ -109,7 +109,7 @@ public void testSerialization() throws Throwable { assertThat(nop.getCount()).isEqualTo(2); // Serialize and continue... - p = (Person) SerializationTestUtils.serializeAndDeserialize(p); + p = SerializationTestUtils.serializeAndDeserialize(p); assertThat(p.getAge()).isEqualTo(newAge); // Remembers count, but we need to get a new reference to nop... nop = (SerializableNopInterceptor) ((Advised) p).getAdvisors()[0].getAdvice(); diff --git a/spring-aop/src/test/java/org/springframework/aop/support/RootClassFilterTests.java b/spring-aop/src/test/java/org/springframework/aop/support/RootClassFilterTests.java index 0c132f327e3b..e09344b35bc8 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/RootClassFilterTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/RootClassFilterTests.java @@ -19,7 +19,7 @@ import org.junit.jupiter.api.Test; import org.springframework.aop.ClassFilter; -import org.springframework.tests.sample.beans.ITestBean; +import org.springframework.beans.testfixture.beans.ITestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-aop/src/test/java/org/springframework/aop/target/CommonsPool2TargetSourceProxyTests.java b/spring-aop/src/test/java/org/springframework/aop/target/CommonsPool2TargetSourceProxyTests.java index 31afac2e099c..6637815d058e 100644 --- a/spring-aop/src/test/java/org/springframework/aop/target/CommonsPool2TargetSourceProxyTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/target/CommonsPool2TargetSourceProxyTests.java @@ -21,11 +21,11 @@ import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.beans.testfixture.beans.ITestBean; import org.springframework.core.io.Resource; -import org.springframework.tests.sample.beans.ITestBean; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * @author Stephane Nicoll diff --git a/spring-aop/src/test/java/org/springframework/aop/target/HotSwappableTargetSourceTests.java b/spring-aop/src/test/java/org/springframework/aop/target/HotSwappableTargetSourceTests.java index ef7bea56dae9..a396adafdcd4 100644 --- a/spring-aop/src/test/java/org/springframework/aop/target/HotSwappableTargetSourceTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/target/HotSwappableTargetSourceTests.java @@ -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. @@ -23,17 +23,17 @@ import org.springframework.aop.framework.Advised; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.support.DefaultPointcutAdvisor; +import org.springframework.aop.testfixture.interceptor.SerializableNopInterceptor; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.tests.aop.interceptor.SerializableNopInterceptor; -import org.springframework.tests.sample.beans.Person; -import org.springframework.tests.sample.beans.SerializablePerson; -import org.springframework.tests.sample.beans.SideEffectBean; -import org.springframework.util.SerializationTestUtils; +import org.springframework.beans.testfixture.beans.Person; +import org.springframework.beans.testfixture.beans.SerializablePerson; +import org.springframework.beans.testfixture.beans.SideEffectBean; +import org.springframework.core.testfixture.io.SerializationTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * @author Rod Johnson @@ -134,7 +134,7 @@ public void testSerialization() throws Exception { hts.swap(sp2); assertThat(p.getName()).isEqualTo(sp2.getName()); - p = (Person) SerializationTestUtils.serializeAndDeserialize(p); + p = SerializationTestUtils.serializeAndDeserialize(p); // We need to get a reference to the client-side targetsource hts = (HotSwappableTargetSource) ((Advised) p).getTargetSource(); assertThat(p.getName()).isEqualTo(sp2.getName()); diff --git a/spring-aop/src/test/java/org/springframework/aop/target/LazyInitTargetSourceTests.java b/spring-aop/src/test/java/org/springframework/aop/target/LazyInitTargetSourceTests.java index 0efaa5165e6f..6c38a0f04fc2 100644 --- a/spring-aop/src/test/java/org/springframework/aop/target/LazyInitTargetSourceTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/target/LazyInitTargetSourceTests.java @@ -22,11 +22,11 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.beans.testfixture.beans.ITestBean; import org.springframework.core.io.Resource; -import org.springframework.tests.sample.beans.ITestBean; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * @author Juergen Hoeller diff --git a/spring-aop/src/test/java/org/springframework/aop/target/PrototypeBasedTargetSourceTests.java b/spring-aop/src/test/java/org/springframework/aop/target/PrototypeBasedTargetSourceTests.java index f051925ad3c6..24af32140c75 100644 --- a/spring-aop/src/test/java/org/springframework/aop/target/PrototypeBasedTargetSourceTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/target/PrototypeBasedTargetSourceTests.java @@ -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. @@ -20,11 +20,12 @@ import org.springframework.aop.TargetSource; import org.springframework.beans.MutablePropertyValues; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.tests.sample.beans.SerializablePerson; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.util.SerializationTestUtils; +import org.springframework.beans.testfixture.beans.SerializablePerson; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.core.testfixture.io.SerializationTestUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -47,14 +48,14 @@ public void testSerializability() throws Exception { MutablePropertyValues pvs = new MutablePropertyValues(); RootBeanDefinition bd = new RootBeanDefinition(SerializablePerson.class); bd.setPropertyValues(pvs); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.registerBeanDefinition("ts", tsBd); bf.registerBeanDefinition("person", bd); TestTargetSource cpts = (TestTargetSource) bf.getBean("ts"); - TargetSource serialized = (TargetSource) SerializationTestUtils.serializeAndDeserialize(cpts); + TargetSource serialized = SerializationTestUtils.serializeAndDeserialize(cpts); boolean condition = serialized instanceof SingletonTargetSource; assertThat(condition).as("Changed to SingletonTargetSource on deserialization").isTrue(); SingletonTargetSource sts = (SingletonTargetSource) serialized; diff --git a/spring-aop/src/test/java/org/springframework/aop/target/PrototypeTargetSourceTests.java b/spring-aop/src/test/java/org/springframework/aop/target/PrototypeTargetSourceTests.java index edcf3379a7cf..7871e1072d18 100644 --- a/spring-aop/src/test/java/org/springframework/aop/target/PrototypeTargetSourceTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/target/PrototypeTargetSourceTests.java @@ -21,10 +21,10 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.tests.sample.beans.SideEffectBean; +import org.springframework.beans.testfixture.beans.SideEffectBean; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * @author Rod Johnson diff --git a/spring-aop/src/test/java/org/springframework/aop/target/ThreadLocalTargetSourceTests.java b/spring-aop/src/test/java/org/springframework/aop/target/ThreadLocalTargetSourceTests.java index bb5e6579d83c..7cb788ed1ac8 100644 --- a/spring-aop/src/test/java/org/springframework/aop/target/ThreadLocalTargetSourceTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/target/ThreadLocalTargetSourceTests.java @@ -21,11 +21,11 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.SideEffectBean; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.SideEffectBean; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * @author Rod Johnson diff --git a/spring-aop/src/test/java/org/springframework/aop/target/dynamic/RefreshableTargetSourceTests.java b/spring-aop/src/test/java/org/springframework/aop/target/dynamic/RefreshableTargetSourceTests.java index dbfe7024c450..593b2b27c021 100644 --- a/spring-aop/src/test/java/org/springframework/aop/target/dynamic/RefreshableTargetSourceTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/target/dynamic/RefreshableTargetSourceTests.java @@ -18,10 +18,10 @@ import org.junit.jupiter.api.Test; -import org.springframework.tests.EnabledForTestGroups; +import org.springframework.core.testfixture.EnabledForTestGroups; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestGroup.PERFORMANCE; +import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING; /** * @author Rob Harrop @@ -77,7 +77,7 @@ public void testWithNoRefreshCheck() throws Exception { } @Test - @EnabledForTestGroups(PERFORMANCE) + @EnabledForTestGroups(LONG_RUNNING) public void testRefreshOverTime() throws Exception { CountingRefreshableTargetSource ts = new CountingRefreshableTargetSource(true); ts.setRefreshCheckDelay(100); diff --git a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-context.xml b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-context.xml index 48da214eb4d6..64f222cdd372 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-context.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerEventTests-context.xml @@ -20,7 +20,7 @@ - + diff --git a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutDuplication.xml b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutDuplication.xml index 1162d714f32e..33693b7c1c40 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutDuplication.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutDuplication.xml @@ -14,7 +14,7 @@ - + diff --git a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutMissing.xml b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutMissing.xml index b47670e08d34..3d2037805e41 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutMissing.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/config/AopNamespaceHandlerPointcutErrorTests-pointcutMissing.xml @@ -14,7 +14,7 @@ - + diff --git a/spring-aop/src/test/resources/org/springframework/aop/interceptor/ExposeInvocationInterceptorTests-context.xml b/spring-aop/src/test/resources/org/springframework/aop/interceptor/ExposeInvocationInterceptorTests-context.xml index 1bfd0c2814b7..a5bf0f37d9c1 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/interceptor/ExposeInvocationInterceptorTests-context.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/interceptor/ExposeInvocationInterceptorTests-context.xml @@ -6,7 +6,7 @@ --> - + @@ -15,7 +15,7 @@ INSTANCE - + diff --git a/spring-aop/src/test/resources/org/springframework/aop/support/RegexpMethodPointcutAdvisorIntegrationTests-context.xml b/spring-aop/src/test/resources/org/springframework/aop/support/RegexpMethodPointcutAdvisorIntegrationTests-context.xml index e67171818b5d..967e42b6868a 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/support/RegexpMethodPointcutAdvisorIntegrationTests-context.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/support/RegexpMethodPointcutAdvisorIntegrationTests-context.xml @@ -4,12 +4,12 @@ - + custom 666 - + @@ -21,15 +21,15 @@ - org.springframework.tests.sample.beans.ITestBean + org.springframework.beans.testfixture.beans.ITestBean settersAdvisor - org.springframework.tests.sample.beans.Person + org.springframework.beans.testfixture.beans.Person - + serializableSettersAdvised @@ -48,7 +48,7 @@ - org.springframework.tests.sample.beans.ITestBean + org.springframework.beans.testfixture.beans.ITestBean true diff --git a/spring-aop/src/test/resources/org/springframework/aop/target/CommonsPool2TargetSourceProxyTests-context.xml b/spring-aop/src/test/resources/org/springframework/aop/target/CommonsPool2TargetSourceProxyTests-context.xml index c3c614d4a634..c0bbd2fe9097 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/target/CommonsPool2TargetSourceProxyTests-context.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/target/CommonsPool2TargetSourceProxyTests-context.xml @@ -3,7 +3,7 @@ - + diff --git a/spring-aop/src/test/resources/org/springframework/aop/target/HotSwappableTargetSourceTests-context.xml b/spring-aop/src/test/resources/org/springframework/aop/target/HotSwappableTargetSourceTests-context.xml index dcc43922efd5..25f39f9f71da 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/target/HotSwappableTargetSourceTests-context.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/target/HotSwappableTargetSourceTests-context.xml @@ -4,11 +4,11 @@ - + 10 - + 20 diff --git a/spring-aop/src/test/resources/org/springframework/aop/target/LazyInitTargetSourceTests-customTarget.xml b/spring-aop/src/test/resources/org/springframework/aop/target/LazyInitTargetSourceTests-customTarget.xml index cec1c980ba5a..70f7406195da 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/target/LazyInitTargetSourceTests-customTarget.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/target/LazyInitTargetSourceTests-customTarget.xml @@ -3,7 +3,7 @@ - + 10 diff --git a/spring-aop/src/test/resources/org/springframework/aop/target/LazyInitTargetSourceTests-singleton.xml b/spring-aop/src/test/resources/org/springframework/aop/target/LazyInitTargetSourceTests-singleton.xml index 1e5c69f5499b..e10984760269 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/target/LazyInitTargetSourceTests-singleton.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/target/LazyInitTargetSourceTests-singleton.xml @@ -3,7 +3,7 @@ - + 10 diff --git a/spring-aop/src/test/resources/org/springframework/aop/target/PrototypeTargetSourceTests-context.xml b/spring-aop/src/test/resources/org/springframework/aop/target/PrototypeTargetSourceTests-context.xml index 4ee787b20fb4..53513ecf1df6 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/target/PrototypeTargetSourceTests-context.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/target/PrototypeTargetSourceTests-context.xml @@ -4,11 +4,11 @@ - + 10 - + 10 @@ -16,7 +16,7 @@ prototypeTest - + debugInterceptor,test diff --git a/spring-aop/src/test/resources/org/springframework/aop/target/ThreadLocalTargetSourceTests-context.xml b/spring-aop/src/test/resources/org/springframework/aop/target/ThreadLocalTargetSourceTests-context.xml index 6400db2f215f..778c8c1d7f0e 100644 --- a/spring-aop/src/test/resources/org/springframework/aop/target/ThreadLocalTargetSourceTests-context.xml +++ b/spring-aop/src/test/resources/org/springframework/aop/target/ThreadLocalTargetSourceTests-context.xml @@ -3,7 +3,7 @@ - + 10 @@ -11,7 +11,7 @@ prototypeTest - + - + Rod - + Kerry diff --git a/spring-aop/src/test/java/org/springframework/tests/aop/advice/CountingAfterReturningAdvice.java b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/CountingAfterReturningAdvice.java similarity index 90% rename from spring-aop/src/test/java/org/springframework/tests/aop/advice/CountingAfterReturningAdvice.java rename to spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/CountingAfterReturningAdvice.java index 2b10ce6408ee..8cfab3ca0127 100644 --- a/spring-aop/src/test/java/org/springframework/tests/aop/advice/CountingAfterReturningAdvice.java +++ b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/CountingAfterReturningAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2019 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.aop.advice; +package org.springframework.aop.testfixture.advice; import java.lang.reflect.Method; diff --git a/spring-aop/src/test/java/org/springframework/tests/aop/advice/CountingBeforeAdvice.java b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/CountingBeforeAdvice.java similarity index 90% rename from spring-aop/src/test/java/org/springframework/tests/aop/advice/CountingBeforeAdvice.java rename to spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/CountingBeforeAdvice.java index 8ccb4cc78387..bf931f6bdfaa 100644 --- a/spring-aop/src/test/java/org/springframework/tests/aop/advice/CountingBeforeAdvice.java +++ b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/CountingBeforeAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2019 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.aop.advice; +package org.springframework.aop.testfixture.advice; import java.lang.reflect.Method; diff --git a/spring-aop/src/test/java/org/springframework/tests/aop/advice/MethodCounter.java b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/MethodCounter.java similarity index 83% rename from spring-aop/src/test/java/org/springframework/tests/aop/advice/MethodCounter.java rename to spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/MethodCounter.java index 9527bdcd4d64..384973bd9aa7 100644 --- a/spring-aop/src/test/java/org/springframework/tests/aop/advice/MethodCounter.java +++ b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/MethodCounter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.aop.advice; +package org.springframework.aop.testfixture.advice; import java.io.Serializable; import java.lang.reflect.Method; @@ -25,6 +25,7 @@ * * @author Rod Johnson * @author Chris Beams + * @author Sam Brannen */ @SuppressWarnings("serial") public class MethodCounter implements Serializable { @@ -39,15 +40,12 @@ protected void count(Method m) { } protected void count(String methodName) { - Integer i = map.get(methodName); - i = (i != null) ? new Integer(i.intValue() + 1) : new Integer(1); - map.put(methodName, i); + map.merge(methodName, 1, (n, m) -> n + 1); ++allCount; } public int getCalls(String methodName) { - Integer i = map.get(methodName); - return (i != null ? i.intValue() : 0); + return map.getOrDefault(methodName, 0); } public int getCalls() { diff --git a/spring-aop/src/test/java/org/springframework/tests/aop/advice/MyThrowsHandler.java b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/MyThrowsHandler.java similarity index 95% rename from spring-aop/src/test/java/org/springframework/tests/aop/advice/MyThrowsHandler.java rename to spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/MyThrowsHandler.java index 5eec90e61634..606101af9c62 100644 --- a/spring-aop/src/test/java/org/springframework/tests/aop/advice/MyThrowsHandler.java +++ b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/MyThrowsHandler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.aop.advice; +package org.springframework.aop.testfixture.advice; import java.io.IOException; import java.lang.reflect.Method; @@ -24,10 +24,12 @@ @SuppressWarnings("serial") public class MyThrowsHandler extends MethodCounter implements ThrowsAdvice { + // Full method signature public void afterThrowing(Method m, Object[] args, Object target, IOException ex) { count("ioException"); } + public void afterThrowing(RemoteException ex) throws Throwable { count("remoteException"); } @@ -36,4 +38,5 @@ public void afterThrowing(RemoteException ex) throws Throwable { public void afterThrowing(Method m, Exception ex) throws Throwable { throw new UnsupportedOperationException("Shouldn't be called"); } + } diff --git a/spring-aop/src/test/java/org/springframework/tests/aop/advice/TimestampIntroductionAdvisor.java b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/TimestampIntroductionAdvisor.java similarity index 88% rename from spring-aop/src/test/java/org/springframework/tests/aop/advice/TimestampIntroductionAdvisor.java rename to spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/TimestampIntroductionAdvisor.java index 8e2d4b8cc23b..5321e5d38110 100644 --- a/spring-aop/src/test/java/org/springframework/tests/aop/advice/TimestampIntroductionAdvisor.java +++ b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/advice/TimestampIntroductionAdvisor.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package org.springframework.tests.aop.advice; +package org.springframework.aop.testfixture.advice; import org.springframework.aop.support.DefaultIntroductionAdvisor; import org.springframework.aop.support.DelegatingIntroductionInterceptor; -import org.springframework.tests.aop.interceptor.TimestampIntroductionInterceptor; +import org.springframework.aop.testfixture.interceptor.TimestampIntroductionInterceptor; /** * @author Rod Johnson diff --git a/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/interceptor/NopInterceptor.java similarity index 96% rename from spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java rename to spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/interceptor/NopInterceptor.java index 5c1855cbd4b6..dd4432467620 100644 --- a/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java +++ b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/interceptor/NopInterceptor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.aop.interceptor; +package org.springframework.aop.testfixture.interceptor; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; diff --git a/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/SerializableNopInterceptor.java b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/interceptor/SerializableNopInterceptor.java similarity index 95% rename from spring-aop/src/test/java/org/springframework/tests/aop/interceptor/SerializableNopInterceptor.java rename to spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/interceptor/SerializableNopInterceptor.java index 7813c87f9f6d..25d9546c53a0 100644 --- a/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/SerializableNopInterceptor.java +++ b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/interceptor/SerializableNopInterceptor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.aop.interceptor; +package org.springframework.aop.testfixture.interceptor; import java.io.Serializable; diff --git a/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/TimestampIntroductionInterceptor.java b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/interceptor/TimestampIntroductionInterceptor.java similarity index 90% rename from spring-aop/src/test/java/org/springframework/tests/aop/interceptor/TimestampIntroductionInterceptor.java rename to spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/interceptor/TimestampIntroductionInterceptor.java index 8e665dba6567..24761350132a 100644 --- a/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/TimestampIntroductionInterceptor.java +++ b/spring-aop/src/testFixtures/java/org/springframework/aop/testfixture/interceptor/TimestampIntroductionInterceptor.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package org.springframework.tests.aop.interceptor; +package org.springframework.aop.testfixture.interceptor; import org.springframework.aop.support.DelegatingIntroductionInterceptor; -import org.springframework.tests.TimeStamped; +import org.springframework.core.testfixture.TimeStamped; @SuppressWarnings("serial") public class TimestampIntroductionInterceptor extends DelegatingIntroductionInterceptor diff --git a/spring-aspects/spring-aspects.gradle b/spring-aspects/spring-aspects.gradle index b30a8107f987..12adbfb7e5f4 100644 --- a/spring-aspects/spring-aspects.gradle +++ b/spring-aspects/spring-aspects.gradle @@ -11,7 +11,6 @@ sourceSets.test.java.srcDirs = files() aspectj.version = dependencyManagement.managedVersions['org.aspectj:aspectjweaver'] dependencies { - aspect(project(":spring-orm")) compile("org.aspectj:aspectjweaver") compileOnly("org.aspectj:aspectjrt") optional(project(":spring-aop")) // for @Async support @@ -24,6 +23,10 @@ dependencies { optional("javax.transaction:javax.transaction-api") // for @javax.transaction.Transactional support testCompile(project(":spring-core")) // for CodeStyleAspect testCompile(project(":spring-test")) + testCompile(testFixtures(project(":spring-context"))) + testCompile(testFixtures(project(":spring-context-support"))) + testCompile(testFixtures(project(":spring-core"))) + testCompile(testFixtures(project(":spring-tx"))) testCompile("javax.mail:javax.mail-api") testCompileOnly("org.aspectj:aspectjrt") } diff --git a/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractInterfaceDrivenDependencyInjectionAspect.aj b/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractInterfaceDrivenDependencyInjectionAspect.aj index f48e0d9252e5..6e049a58a045 100644 --- a/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractInterfaceDrivenDependencyInjectionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AbstractInterfaceDrivenDependencyInjectionAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 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. @@ -67,6 +67,7 @@ import java.io.Serializable; * @since 2.5.2 */ public abstract aspect AbstractInterfaceDrivenDependencyInjectionAspect extends AbstractDependencyInjectionAspect { + /** * Select initialization join point as object construction */ diff --git a/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AnnotationBeanConfigurerAspect.aj b/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AnnotationBeanConfigurerAspect.aj index 98599fb2e4e2..923e459b4438 100644 --- a/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AnnotationBeanConfigurerAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/beans/factory/aspectj/AnnotationBeanConfigurerAspect.aj @@ -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. @@ -47,22 +47,26 @@ import org.springframework.beans.factory.wiring.BeanConfigurerSupport; public aspect AnnotationBeanConfigurerAspect extends AbstractInterfaceDrivenDependencyInjectionAspect implements BeanFactoryAware, InitializingBean, DisposableBean { - private BeanConfigurerSupport beanConfigurerSupport = new BeanConfigurerSupport(); + private final BeanConfigurerSupport beanConfigurerSupport = new BeanConfigurerSupport(); + @Override public void setBeanFactory(BeanFactory beanFactory) { this.beanConfigurerSupport.setBeanWiringInfoResolver(new AnnotationBeanWiringInfoResolver()); this.beanConfigurerSupport.setBeanFactory(beanFactory); } + @Override public void afterPropertiesSet() { this.beanConfigurerSupport.afterPropertiesSet(); } + @Override public void configureBean(Object bean) { this.beanConfigurerSupport.configureBean(bean); } + @Override public void destroy() { this.beanConfigurerSupport.destroy(); } diff --git a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJCachingConfiguration.java b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJCachingConfiguration.java index 4f0a383d55a6..e48a0f669a74 100644 --- a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJCachingConfiguration.java +++ b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJCachingConfiguration.java @@ -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. @@ -34,7 +34,7 @@ * @see org.springframework.cache.annotation.EnableCaching * @see org.springframework.cache.annotation.CachingConfigurationSelector */ -@Configuration +@Configuration(proxyBeanMethods = false) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class AspectJCachingConfiguration extends AbstractCachingConfiguration { diff --git a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJJCacheConfiguration.java b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJJCacheConfiguration.java index 524be193e2df..3468889517f5 100644 --- a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJJCacheConfiguration.java +++ b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJJCacheConfiguration.java @@ -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. @@ -19,6 +19,7 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.cache.config.CacheManagementConfigUtils; import org.springframework.cache.jcache.config.AbstractJCacheConfiguration; +import org.springframework.cache.jcache.interceptor.JCacheOperationSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Role; @@ -33,15 +34,15 @@ * @see org.springframework.cache.annotation.EnableCaching * @see org.springframework.cache.annotation.CachingConfigurationSelector */ -@Configuration +@Configuration(proxyBeanMethods = false) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class AspectJJCacheConfiguration extends AbstractJCacheConfiguration { @Bean(name = CacheManagementConfigUtils.JCACHE_ASPECT_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - public JCacheCacheAspect cacheAspect() { + public JCacheCacheAspect cacheAspect(JCacheOperationSource jCacheOperationSource) { JCacheCacheAspect cacheAspect = JCacheCacheAspect.aspectOf(); - cacheAspect.setCacheOperationSource(cacheOperationSource()); + cacheAspect.setCacheOperationSource(jCacheOperationSource); return cacheAspect; } diff --git a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AspectJAsyncConfiguration.java b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AspectJAsyncConfiguration.java index ef93148652ac..c6cdada36567 100644 --- a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AspectJAsyncConfiguration.java +++ b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AspectJAsyncConfiguration.java @@ -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. @@ -36,7 +36,8 @@ * @see org.springframework.scheduling.annotation.AsyncConfigurationSelector * @see org.springframework.scheduling.annotation.ProxyAsyncConfiguration */ -@Configuration +@Configuration(proxyBeanMethods = false) +@Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class AspectJAsyncConfiguration extends AbstractAsyncConfiguration { @Bean(name = TaskManagementConfigUtils.ASYNC_EXECUTION_ASPECT_BEAN_NAME) diff --git a/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AbstractTransactionAspect.aj b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AbstractTransactionAspect.aj index aed8e4ab65a8..782ca35e0777 100644 --- a/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AbstractTransactionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AbstractTransactionAspect.aj @@ -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") diff --git a/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AspectJJtaTransactionManagementConfiguration.java b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AspectJJtaTransactionManagementConfiguration.java index caa7cc3c2f72..ec733d3cf2b9 100644 --- a/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AspectJJtaTransactionManagementConfiguration.java +++ b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AspectJJtaTransactionManagementConfiguration.java @@ -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. @@ -36,6 +36,7 @@ * @see TransactionManagementConfigurationSelector */ @Configuration +@Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class AspectJJtaTransactionManagementConfiguration extends AspectJTransactionManagementConfiguration { @Bean(name = TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_BEAN_NAME) diff --git a/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AspectJTransactionManagementConfiguration.java b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AspectJTransactionManagementConfiguration.java index 2784a733e7c2..2c99c3050744 100644 --- a/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AspectJTransactionManagementConfiguration.java +++ b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AspectJTransactionManagementConfiguration.java @@ -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,7 @@ * @see AspectJJtaTransactionManagementConfiguration */ @Configuration +@Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class AspectJTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME) diff --git a/spring-aspects/src/test/java/org/springframework/cache/aspectj/AbstractCacheAnnotationTests.java b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AbstractCacheAnnotationTests.java new file mode 100644 index 000000000000..81dcf6b1e224 --- /dev/null +++ b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AbstractCacheAnnotationTests.java @@ -0,0 +1,882 @@ +/* + * 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.cache.aspectj; + +import java.util.Collection; +import java.util.UUID; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.aop.framework.AopProxyUtils; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.config.AnnotatedClassCacheableService; +import org.springframework.cache.config.CacheableService; +import org.springframework.cache.config.TestEntity; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.testfixture.cache.SomeCustomKeyGenerator; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIOException; + +/** + * Copy of the shared {@code AbstractCacheAnnotationTests}: necessary + * due to issues with Gradle test fixtures and AspectJ configuration + * in the Gradle build. + * + *

Abstract cache annotation tests (containing several reusable methods). + * + * @author Costin Leau + * @author Chris Beams + * @author Phillip Webb + * @author Stephane Nicoll + */ +public abstract class AbstractCacheAnnotationTests { + + protected ConfigurableApplicationContext ctx; + + protected CacheableService cs; + + protected CacheableService ccs; + + protected CacheManager cm; + + + /** + * @return a refreshed application context + */ + protected abstract ConfigurableApplicationContext getApplicationContext(); + + + @BeforeEach + public void setup() { + this.ctx = getApplicationContext(); + this.cs = ctx.getBean("service", CacheableService.class); + this.ccs = ctx.getBean("classService", CacheableService.class); + this.cm = ctx.getBean("cacheManager", CacheManager.class); + + Collection cn = this.cm.getCacheNames(); + assertThat(cn).containsOnly("testCache", "secondary", "primary"); + } + + @AfterEach + public void close() { + if (this.ctx != null) { + this.ctx.close(); + } + } + + + protected void testCacheable(CacheableService service) { + Object o1 = new Object(); + + Object r1 = service.cache(o1); + Object r2 = service.cache(o1); + Object r3 = service.cache(o1); + + assertThat(r2).isSameAs(r1); + assertThat(r3).isSameAs(r1); + } + + protected void testCacheableNull(CacheableService service) { + Object o1 = new Object(); + assertThat(this.cm.getCache("testCache").get(o1)).isNull(); + + Object r1 = service.cacheNull(o1); + Object r2 = service.cacheNull(o1); + Object r3 = service.cacheNull(o1); + + assertThat(r2).isSameAs(r1); + assertThat(r3).isSameAs(r1); + + assertThat(this.cm.getCache("testCache")).as("testCache").isNotNull(); + assertThat(this.cm.getCache("testCache").get(o1)).as("cached object").isNotNull(); + assertThat(this.cm.getCache("testCache").get(o1).get()).isEqualTo(r3); + assertThat(r3).as("Cached value should be null").isNull(); + } + + protected void testCacheableSync(CacheableService service) { + Object o1 = new Object(); + + Object r1 = service.cacheSync(o1); + Object r2 = service.cacheSync(o1); + Object r3 = service.cacheSync(o1); + + assertThat(r2).isSameAs(r1); + assertThat(r3).isSameAs(r1); + } + + protected void testCacheableSyncNull(CacheableService service) { + Object o1 = new Object(); + assertThat(this.cm.getCache("testCache").get(o1)).isNull(); + + Object r1 = service.cacheSyncNull(o1); + Object r2 = service.cacheSyncNull(o1); + Object r3 = service.cacheSyncNull(o1); + + assertThat(r2).isSameAs(r1); + assertThat(r3).isSameAs(r1); + + assertThat(this.cm.getCache("testCache").get(o1).get()).isEqualTo(r3); + assertThat(r3).as("Cached value should be null").isNull(); + } + + protected void testEvict(CacheableService service, boolean successExpected) { + Cache cache = this.cm.getCache("testCache"); + + Object o1 = new Object(); + cache.putIfAbsent(o1, -1L); + Object r1 = service.cache(o1); + + service.evict(o1, null); + if (successExpected) { + assertThat(cache.get(o1)).isNull(); + } + else { + assertThat(cache.get(o1)).isNotNull(); + } + + Object r2 = service.cache(o1); + if (successExpected) { + assertThat(r2).isNotSameAs(r1); + } + else { + assertThat(r2).isSameAs(r1); + } + } + + protected void testEvictEarly(CacheableService service) { + Cache cache = this.cm.getCache("testCache"); + + Object o1 = new Object(); + cache.putIfAbsent(o1, -1L); + Object r1 = service.cache(o1); + + try { + service.evictEarly(o1); + } + catch (RuntimeException ex) { + // expected + } + assertThat(cache.get(o1)).isNull(); + + Object r2 = service.cache(o1); + assertThat(r2).isNotSameAs(r1); + } + + protected void testEvictException(CacheableService service) { + Object o1 = new Object(); + Object r1 = service.cache(o1); + + try { + service.evictWithException(o1); + } + catch (RuntimeException ex) { + // expected + } + // exception occurred, eviction skipped, data should still be in the cache + Object r2 = service.cache(o1); + assertThat(r2).isSameAs(r1); + } + + protected void testEvictWithKey(CacheableService service) { + Object o1 = new Object(); + Object r1 = service.cache(o1); + + service.evict(o1, null); + Object r2 = service.cache(o1); + assertThat(r2).isNotSameAs(r1); + } + + protected void testEvictWithKeyEarly(CacheableService service) { + Object o1 = new Object(); + Object r1 = service.cache(o1); + + try { + service.evictEarly(o1); + } + catch (Exception ex) { + // expected + } + Object r2 = service.cache(o1); + assertThat(r2).isNotSameAs(r1); + } + + protected void testEvictAll(CacheableService service, boolean successExpected) { + Cache cache = this.cm.getCache("testCache"); + + Object o1 = new Object(); + Object o2 = new Object(); + cache.putIfAbsent(o1, -1L); + cache.putIfAbsent(o2, -2L); + + Object r1 = service.cache(o1); + Object r2 = service.cache(o2); + assertThat(r2).isNotSameAs(r1); + + service.evictAll(new Object()); + if (successExpected) { + assertThat(cache.get(o1)).isNull(); + assertThat(cache.get(o2)).isNull(); + } + else { + assertThat(cache.get(o1)).isNotNull(); + assertThat(cache.get(o2)).isNotNull(); + } + + Object r3 = service.cache(o1); + Object r4 = service.cache(o2); + if (successExpected) { + assertThat(r3).isNotSameAs(r1); + assertThat(r4).isNotSameAs(r2); + } + else { + assertThat(r3).isSameAs(r1); + assertThat(r4).isSameAs(r2); + } + } + + protected void testEvictAllEarly(CacheableService service) { + Cache cache = this.cm.getCache("testCache"); + + Object o1 = new Object(); + Object o2 = new Object(); + cache.putIfAbsent(o1, -1L); + cache.putIfAbsent(o2, -2L); + + Object r1 = service.cache(o1); + Object r2 = service.cache(o2); + assertThat(r2).isNotSameAs(r1); + + try { + service.evictAllEarly(new Object()); + } + catch (Exception ex) { + // expected + } + assertThat(cache.get(o1)).isNull(); + assertThat(cache.get(o2)).isNull(); + + Object r3 = service.cache(o1); + Object r4 = service.cache(o2); + assertThat(r3).isNotSameAs(r1); + assertThat(r4).isNotSameAs(r2); + } + + protected void testConditionalExpression(CacheableService service) { + Object r1 = service.conditional(4); + Object r2 = service.conditional(4); + + assertThat(r2).isNotSameAs(r1); + + Object r3 = service.conditional(3); + Object r4 = service.conditional(3); + + assertThat(r4).isSameAs(r3); + } + + protected void testConditionalExpressionSync(CacheableService service) { + Object r1 = service.conditionalSync(4); + Object r2 = service.conditionalSync(4); + + assertThat(r2).isNotSameAs(r1); + + Object r3 = service.conditionalSync(3); + Object r4 = service.conditionalSync(3); + + assertThat(r4).isSameAs(r3); + } + + protected void testUnlessExpression(CacheableService service) { + Cache cache = this.cm.getCache("testCache"); + cache.clear(); + service.unless(10); + service.unless(11); + assertThat(cache.get(10).get()).isEqualTo(10L); + assertThat(cache.get(11)).isNull(); + } + + protected void testKeyExpression(CacheableService service) { + Object r1 = service.key(5, 1); + Object r2 = service.key(5, 2); + + assertThat(r2).isSameAs(r1); + + Object r3 = service.key(1, 5); + Object r4 = service.key(2, 5); + + assertThat(r4).isNotSameAs(r3); + } + + protected void testVarArgsKey(CacheableService service) { + Object r1 = service.varArgsKey(1, 2, 3); + Object r2 = service.varArgsKey(1, 2, 3); + + assertThat(r2).isSameAs(r1); + + Object r3 = service.varArgsKey(1, 2, 3); + Object r4 = service.varArgsKey(1, 2); + + assertThat(r4).isNotSameAs(r3); + } + + protected void testNullValue(CacheableService service) { + Object key = new Object(); + assertThat(service.nullValue(key)).isNull(); + int nr = service.nullInvocations().intValue(); + assertThat(service.nullValue(key)).isNull(); + assertThat(service.nullInvocations().intValue()).isEqualTo(nr); + assertThat(service.nullValue(new Object())).isNull(); + assertThat(service.nullInvocations().intValue()).isEqualTo(nr + 1); + } + + protected void testMethodName(CacheableService service, String keyName) { + Object key = new Object(); + Object r1 = service.name(key); + assertThat(service.name(key)).isSameAs(r1); + Cache cache = this.cm.getCache("testCache"); + // assert the method name is used + assertThat(cache.get(keyName)).isNotNull(); + } + + protected void testRootVars(CacheableService service) { + Object key = new Object(); + Object r1 = service.rootVars(key); + assertThat(service.rootVars(key)).isSameAs(r1); + Cache cache = this.cm.getCache("testCache"); + // assert the method name is used + String expectedKey = "rootVarsrootVars" + AopProxyUtils.ultimateTargetClass(service) + service; + assertThat(cache.get(expectedKey)).isNotNull(); + } + + protected void testCheckedThrowable(CacheableService service) { + String arg = UUID.randomUUID().toString(); + assertThatIOException().isThrownBy(() -> + service.throwChecked(arg)) + .withMessage(arg); + } + + protected void testUncheckedThrowable(CacheableService service) { + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> + service.throwUnchecked(1L)) + .withMessage("1"); + } + + protected void testCheckedThrowableSync(CacheableService service) { + String arg = UUID.randomUUID().toString(); + assertThatIOException().isThrownBy(() -> + service.throwCheckedSync(arg)) + .withMessage(arg); + } + + protected void testUncheckedThrowableSync(CacheableService service) { + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> + service.throwUncheckedSync(1L)) + .withMessage("1"); + } + + protected void testNullArg(CacheableService service) { + Object r1 = service.cache(null); + assertThat(service.cache(null)).isSameAs(r1); + } + + protected void testCacheUpdate(CacheableService service) { + Object o = new Object(); + Cache cache = this.cm.getCache("testCache"); + assertThat(cache.get(o)).isNull(); + Object r1 = service.update(o); + assertThat(cache.get(o).get()).isSameAs(r1); + + o = new Object(); + assertThat(cache.get(o)).isNull(); + Object r2 = service.update(o); + assertThat(cache.get(o).get()).isSameAs(r2); + } + + protected void testConditionalCacheUpdate(CacheableService service) { + int one = 1; + int three = 3; + + Cache cache = this.cm.getCache("testCache"); + assertThat(Integer.parseInt(service.conditionalUpdate(one).toString())).isEqualTo(one); + assertThat(cache.get(one)).isNull(); + + assertThat(Integer.parseInt(service.conditionalUpdate(three).toString())).isEqualTo(three); + assertThat(Integer.parseInt(cache.get(three).get().toString())).isEqualTo(three); + } + + protected void testMultiCache(CacheableService service) { + Object o1 = new Object(); + Object o2 = new Object(); + + Cache primary = this.cm.getCache("primary"); + Cache secondary = this.cm.getCache("secondary"); + + assertThat(primary.get(o1)).isNull(); + assertThat(secondary.get(o1)).isNull(); + Object r1 = service.multiCache(o1); + assertThat(primary.get(o1).get()).isSameAs(r1); + assertThat(secondary.get(o1).get()).isSameAs(r1); + + Object r2 = service.multiCache(o1); + Object r3 = service.multiCache(o1); + + assertThat(r2).isSameAs(r1); + assertThat(r3).isSameAs(r1); + + assertThat(primary.get(o2)).isNull(); + assertThat(secondary.get(o2)).isNull(); + Object r4 = service.multiCache(o2); + assertThat(primary.get(o2).get()).isSameAs(r4); + assertThat(secondary.get(o2).get()).isSameAs(r4); + } + + protected void testMultiEvict(CacheableService service) { + Object o1 = new Object(); + Object o2 = o1.toString() + "A"; + + + Object r1 = service.multiCache(o1); + Object r2 = service.multiCache(o1); + + Cache primary = this.cm.getCache("primary"); + Cache secondary = this.cm.getCache("secondary"); + + primary.put(o2, o2); + assertThat(r2).isSameAs(r1); + assertThat(primary.get(o1).get()).isSameAs(r1); + assertThat(secondary.get(o1).get()).isSameAs(r1); + + service.multiEvict(o1); + assertThat(primary.get(o1)).isNull(); + assertThat(secondary.get(o1)).isNull(); + assertThat(primary.get(o2)).isNull(); + + Object r3 = service.multiCache(o1); + Object r4 = service.multiCache(o1); + assertThat(r3).isNotSameAs(r1); + assertThat(r4).isSameAs(r3); + + assertThat(primary.get(o1).get()).isSameAs(r3); + assertThat(secondary.get(o1).get()).isSameAs(r4); + } + + protected void testMultiPut(CacheableService service) { + Object o = 1; + + Cache primary = this.cm.getCache("primary"); + Cache secondary = this.cm.getCache("secondary"); + + assertThat(primary.get(o)).isNull(); + assertThat(secondary.get(o)).isNull(); + Object r1 = service.multiUpdate(o); + assertThat(primary.get(o).get()).isSameAs(r1); + assertThat(secondary.get(o).get()).isSameAs(r1); + + o = 2; + assertThat(primary.get(o)).isNull(); + assertThat(secondary.get(o)).isNull(); + Object r2 = service.multiUpdate(o); + assertThat(primary.get(o).get()).isSameAs(r2); + assertThat(secondary.get(o).get()).isSameAs(r2); + } + + protected void testPutRefersToResult(CacheableService service) { + Long id = Long.MIN_VALUE; + TestEntity entity = new TestEntity(); + Cache primary = this.cm.getCache("primary"); + assertThat(primary.get(id)).isNull(); + assertThat(entity.getId()).isNull(); + service.putRefersToResult(entity); + assertThat(primary.get(id).get()).isSameAs(entity); + } + + protected void testMultiCacheAndEvict(CacheableService service) { + String methodName = "multiCacheAndEvict"; + + Cache primary = this.cm.getCache("primary"); + Cache secondary = this.cm.getCache("secondary"); + Object key = 1; + + secondary.put(key, key); + + assertThat(secondary.get(methodName)).isNull(); + assertThat(secondary.get(key).get()).isSameAs(key); + + Object r1 = service.multiCacheAndEvict(key); + assertThat(service.multiCacheAndEvict(key)).isSameAs(r1); + + // assert the method name is used + assertThat(primary.get(methodName).get()).isSameAs(r1); + assertThat(secondary.get(methodName)).isNull(); + assertThat(secondary.get(key)).isNull(); + } + + protected void testMultiConditionalCacheAndEvict(CacheableService service) { + Cache primary = this.cm.getCache("primary"); + Cache secondary = this.cm.getCache("secondary"); + Object key = 1; + + secondary.put(key, key); + + assertThat(primary.get(key)).isNull(); + assertThat(secondary.get(key).get()).isSameAs(key); + + Object r1 = service.multiConditionalCacheAndEvict(key); + Object r3 = service.multiConditionalCacheAndEvict(key); + + assertThat(!r1.equals(r3)).isTrue(); + assertThat(primary.get(key)).isNull(); + + Object key2 = 3; + Object r2 = service.multiConditionalCacheAndEvict(key2); + assertThat(service.multiConditionalCacheAndEvict(key2)).isSameAs(r2); + + // assert the method name is used + assertThat(primary.get(key2).get()).isSameAs(r2); + assertThat(secondary.get(key2)).isNull(); + } + + @Test + public void testCacheable() { + testCacheable(this.cs); + } + + @Test + public void testCacheableNull() { + testCacheableNull(this.cs); + } + + @Test + public void testCacheableSync() { + testCacheableSync(this.cs); + } + + @Test + public void testCacheableSyncNull() { + testCacheableSyncNull(this.cs); + } + + @Test + public void testEvict() { + testEvict(this.cs, true); + } + + @Test + public void testEvictEarly() { + testEvictEarly(this.cs); + } + + @Test + public void testEvictWithException() { + testEvictException(this.cs); + } + + @Test + public void testEvictAll() { + testEvictAll(this.cs, true); + } + + @Test + public void testEvictAllEarly() { + testEvictAllEarly(this.cs); + } + + @Test + public void testEvictWithKey() { + testEvictWithKey(this.cs); + } + + @Test + public void testEvictWithKeyEarly() { + testEvictWithKeyEarly(this.cs); + } + + @Test + public void testConditionalExpression() { + testConditionalExpression(this.cs); + } + + @Test + public void testConditionalExpressionSync() { + testConditionalExpressionSync(this.cs); + } + + @Test + public void testUnlessExpression() { + testUnlessExpression(this.cs); + } + + @Test + public void testClassCacheUnlessExpression() { + testUnlessExpression(this.cs); + } + + @Test + public void testKeyExpression() { + testKeyExpression(this.cs); + } + + @Test + public void testVarArgsKey() { + testVarArgsKey(this.cs); + } + + @Test + public void testClassCacheCacheable() { + testCacheable(this.ccs); + } + + @Test + public void testClassCacheEvict() { + testEvict(this.ccs, true); + } + + @Test + public void testClassEvictEarly() { + testEvictEarly(this.ccs); + } + + @Test + public void testClassEvictAll() { + testEvictAll(this.ccs, true); + } + + @Test + public void testClassEvictWithException() { + testEvictException(this.ccs); + } + + @Test + public void testClassCacheEvictWithWKey() { + testEvictWithKey(this.ccs); + } + + @Test + public void testClassEvictWithKeyEarly() { + testEvictWithKeyEarly(this.ccs); + } + + @Test + public void testNullValue() { + testNullValue(this.cs); + } + + @Test + public void testClassNullValue() { + Object key = new Object(); + assertThat(this.ccs.nullValue(key)).isNull(); + int nr = this.ccs.nullInvocations().intValue(); + assertThat(this.ccs.nullValue(key)).isNull(); + assertThat(this.ccs.nullInvocations().intValue()).isEqualTo(nr); + assertThat(this.ccs.nullValue(new Object())).isNull(); + // the check method is also cached + assertThat(this.ccs.nullInvocations().intValue()).isEqualTo(nr); + assertThat(AnnotatedClassCacheableService.nullInvocations.intValue()).isEqualTo(nr + 1); + } + + @Test + public void testMethodName() { + testMethodName(this.cs, "name"); + } + + @Test + public void testClassMethodName() { + testMethodName(this.ccs, "nametestCache"); + } + + @Test + public void testRootVars() { + testRootVars(this.cs); + } + + @Test + public void testClassRootVars() { + testRootVars(this.ccs); + } + + @Test + public void testCustomKeyGenerator() { + Object param = new Object(); + Object r1 = this.cs.customKeyGenerator(param); + assertThat(this.cs.customKeyGenerator(param)).isSameAs(r1); + Cache cache = this.cm.getCache("testCache"); + // Checks that the custom keyGenerator was used + Object expectedKey = SomeCustomKeyGenerator.generateKey("customKeyGenerator", param); + assertThat(cache.get(expectedKey)).isNotNull(); + } + + @Test + public void testUnknownCustomKeyGenerator() { + Object param = new Object(); + assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() -> + this.cs.unknownCustomKeyGenerator(param)); + } + + @Test + public void testCustomCacheManager() { + CacheManager customCm = this.ctx.getBean("customCacheManager", CacheManager.class); + Object key = new Object(); + Object r1 = this.cs.customCacheManager(key); + assertThat(this.cs.customCacheManager(key)).isSameAs(r1); + + Cache cache = customCm.getCache("testCache"); + assertThat(cache.get(key)).isNotNull(); + } + + @Test + public void testUnknownCustomCacheManager() { + Object param = new Object(); + assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() -> + this.cs.unknownCustomCacheManager(param)); + } + + @Test + public void testNullArg() { + testNullArg(this.cs); + } + + @Test + public void testClassNullArg() { + testNullArg(this.ccs); + } + + @Test + public void testCheckedException() { + testCheckedThrowable(this.cs); + } + + @Test + public void testClassCheckedException() { + testCheckedThrowable(this.ccs); + } + + @Test + public void testCheckedExceptionSync() { + testCheckedThrowableSync(this.cs); + } + + @Test + public void testClassCheckedExceptionSync() { + testCheckedThrowableSync(this.ccs); + } + + @Test + public void testUncheckedException() { + testUncheckedThrowable(this.cs); + } + + @Test + public void testClassUncheckedException() { + testUncheckedThrowable(this.ccs); + } + + @Test + public void testUncheckedExceptionSync() { + testUncheckedThrowableSync(this.cs); + } + + @Test + public void testClassUncheckedExceptionSync() { + testUncheckedThrowableSync(this.ccs); + } + + @Test + public void testUpdate() { + testCacheUpdate(this.cs); + } + + @Test + public void testClassUpdate() { + testCacheUpdate(this.ccs); + } + + @Test + public void testConditionalUpdate() { + testConditionalCacheUpdate(this.cs); + } + + @Test + public void testClassConditionalUpdate() { + testConditionalCacheUpdate(this.ccs); + } + + @Test + public void testMultiCache() { + testMultiCache(this.cs); + } + + @Test + public void testClassMultiCache() { + testMultiCache(this.ccs); + } + + @Test + public void testMultiEvict() { + testMultiEvict(this.cs); + } + + @Test + public void testClassMultiEvict() { + testMultiEvict(this.ccs); + } + + @Test + public void testMultiPut() { + testMultiPut(this.cs); + } + + @Test + public void testClassMultiPut() { + testMultiPut(this.ccs); + } + + @Test + public void testPutRefersToResult() { + testPutRefersToResult(this.cs); + } + + @Test + public void testClassPutRefersToResult() { + testPutRefersToResult(this.ccs); + } + + @Test + public void testMultiCacheAndEvict() { + testMultiCacheAndEvict(this.cs); + } + + @Test + public void testClassMultiCacheAndEvict() { + testMultiCacheAndEvict(this.ccs); + } + + @Test + public void testMultiConditionalCacheAndEvict() { + testMultiConditionalCacheAndEvict(this.cs); + } + + @Test + public void testClassMultiConditionalCacheAndEvict() { + testMultiConditionalCacheAndEvict(this.ccs); + } + +} diff --git a/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJCacheAnnotationTests.java b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJCacheAnnotationTests.java index e952eacd3b00..4601c0ea814c 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJCacheAnnotationTests.java +++ b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJCacheAnnotationTests.java @@ -19,7 +19,6 @@ import org.junit.jupiter.api.Test; import org.springframework.cache.Cache; -import org.springframework.cache.config.AbstractCacheAnnotationTests; import org.springframework.cache.config.CacheableService; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; diff --git a/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java index 57b9b90f5596..7ca1037efbc4 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java +++ b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java @@ -22,14 +22,8 @@ import org.springframework.beans.factory.BeanCreationException; import org.springframework.cache.CacheManager; -import org.springframework.cache.CacheTestUtils; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; -import org.springframework.cache.config.AnnotatedClassCacheableService; -import org.springframework.cache.config.CacheableService; -import org.springframework.cache.config.DefaultCacheableService; -import org.springframework.cache.config.SomeCustomKeyGenerator; -import org.springframework.cache.config.SomeKeyGenerator; import org.springframework.cache.interceptor.CacheErrorHandler; import org.springframework.cache.interceptor.CacheResolver; import org.springframework.cache.interceptor.KeyGenerator; @@ -42,6 +36,12 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.testfixture.cache.CacheTestUtils; +import org.springframework.context.testfixture.cache.SomeCustomKeyGenerator; +import org.springframework.context.testfixture.cache.SomeKeyGenerator; +import org.springframework.context.testfixture.cache.beans.AnnotatedClassCacheableService; +import org.springframework.context.testfixture.cache.beans.CacheableService; +import org.springframework.context.testfixture.cache.beans.DefaultCacheableService; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingTests.java b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingTests.java index 7857394de16f..7e693d6ea13a 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingTests.java +++ b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2019 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,15 +17,11 @@ package org.springframework.cache.aspectj; import org.springframework.cache.CacheManager; -import org.springframework.cache.CacheTestUtils; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; -import org.springframework.cache.config.AbstractCacheAnnotationTests; import org.springframework.cache.config.AnnotatedClassCacheableService; import org.springframework.cache.config.CacheableService; import org.springframework.cache.config.DefaultCacheableService; -import org.springframework.cache.config.SomeCustomKeyGenerator; -import org.springframework.cache.config.SomeKeyGenerator; import org.springframework.cache.interceptor.CacheErrorHandler; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.SimpleCacheErrorHandler; @@ -34,6 +30,9 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.testfixture.cache.CacheTestUtils; +import org.springframework.context.testfixture.cache.SomeCustomKeyGenerator; +import org.springframework.context.testfixture.cache.SomeKeyGenerator; /** * @author Stephane Nicoll diff --git a/spring-aspects/src/test/java/org/springframework/cache/aspectj/JCacheAspectJJavaConfigTests.java b/spring-aspects/src/test/java/org/springframework/cache/aspectj/JCacheAspectJJavaConfigTests.java index c48ad735e6a9..dc278365d727 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/aspectj/JCacheAspectJJavaConfigTests.java +++ b/spring-aspects/src/test/java/org/springframework/cache/aspectj/JCacheAspectJJavaConfigTests.java @@ -23,13 +23,13 @@ import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.concurrent.ConcurrentMapCache; import org.springframework.cache.config.AnnotatedJCacheableService; -import org.springframework.cache.jcache.config.AbstractJCacheAnnotationTests; import org.springframework.cache.support.SimpleCacheManager; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AdviceMode; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.contextsupport.testfixture.jcache.AbstractJCacheAnnotationTests; /** * @author Stephane Nicoll diff --git a/spring-aspects/src/test/java/org/springframework/cache/aspectj/JCacheAspectJNamespaceConfigTests.java b/spring-aspects/src/test/java/org/springframework/cache/aspectj/JCacheAspectJNamespaceConfigTests.java index a3e924e35502..81ba6d0faff9 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/aspectj/JCacheAspectJNamespaceConfigTests.java +++ b/spring-aspects/src/test/java/org/springframework/cache/aspectj/JCacheAspectJNamespaceConfigTests.java @@ -16,9 +16,9 @@ package org.springframework.cache.aspectj; -import org.springframework.cache.jcache.config.AbstractJCacheAnnotationTests; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; +import org.springframework.contextsupport.testfixture.jcache.AbstractJCacheAnnotationTests; /** * @author Stephane Nicoll diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java index 5872dffc7808..fa494c921280 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java @@ -25,6 +25,10 @@ import org.springframework.cache.annotation.Caching; /** + * Copy of the shared {@code AbstractCacheAnnotationTests}: necessary + * due to issues with Gradle test fixtures and AspectJ configuration + * in the Gradle build. + * * @author Costin Leau * @author Phillip Webb * @author Stephane Nicoll diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedJCacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedJCacheableService.java index 299f76bf8f61..0b004f46b910 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedJCacheableService.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedJCacheableService.java @@ -30,9 +30,9 @@ import org.springframework.cache.Cache; import org.springframework.cache.interceptor.SimpleKeyGenerator; -import org.springframework.cache.jcache.config.JCacheableService; -import org.springframework.cache.jcache.support.TestableCacheKeyGenerator; -import org.springframework.cache.jcache.support.TestableCacheResolverFactory; +import org.springframework.contextsupport.testfixture.cache.TestableCacheKeyGenerator; +import org.springframework.contextsupport.testfixture.cache.TestableCacheResolverFactory; +import org.springframework.contextsupport.testfixture.jcache.JCacheableService; /** * Repository sample with a @CacheDefaults annotation diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/CacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/CacheableService.java index e5ab4a5f94cc..3ec6212bac60 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/CacheableService.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/CacheableService.java @@ -17,7 +17,11 @@ package org.springframework.cache.config; /** - * Basic service interface for caching tests. + * Copy of the shared {@code CacheableService}: necessary + * due to issues with Gradle test fixtures and AspectJ configuration + * in the Gradle build. + * + *

Basic service interface for caching tests. * * @author Costin Leau * @author Phillip Webb diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java index 7df2bb4ea27d..47a3a83a34a1 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java @@ -25,7 +25,11 @@ import org.springframework.cache.annotation.Caching; /** - * Simple cacheable service. + * Copy of the shared {@code DefaultCacheableService}: necessary + * due to issues with Gradle test fixtures and AspectJ configuration + * in the Gradle build. + * + *

Simple cacheable service. * * @author Costin Leau * @author Phillip Webb diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/TestEntity.java b/spring-aspects/src/test/java/org/springframework/cache/config/TestEntity.java index 087019a37641..b308741ee147 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/TestEntity.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/TestEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,11 @@ import org.springframework.util.ObjectUtils; /** - * Simple test entity for use with caching tests. + * Copy of the shared {@code TestEntity}: necessary + * due to issues with Gradle test fixtures and AspectJ configuration + * in the Gradle build. + * + *

Simple test entity for use with caching tests. * * @author Michael Plod */ @@ -53,5 +57,4 @@ public boolean equals(Object obj) { } return false; } - } diff --git a/spring-aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspectTests.java b/spring-aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspectTests.java index 62dceafaede3..624d96a27709 100644 --- a/spring-aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspectTests.java +++ b/spring-aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspectTests.java @@ -30,15 +30,15 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.task.SimpleAsyncTaskExecutor; +import org.springframework.core.testfixture.EnabledForTestGroups; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; -import org.springframework.tests.EnabledForTestGroups; import org.springframework.util.ReflectionUtils; import org.springframework.util.concurrent.ListenableFuture; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestGroup.PERFORMANCE; +import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING; /** * Unit tests for {@link AnnotationAsyncExecutionAspect}. @@ -46,7 +46,7 @@ * @author Ramnivas Laddad * @author Stephane Nicoll */ -@EnabledForTestGroups(PERFORMANCE) +@EnabledForTestGroups(LONG_RUNNING) public class AnnotationAsyncExecutionAspectTests { private static final long WAIT_TIME = 1000; //milliseconds diff --git a/spring-aspects/src/test/java/org/springframework/transaction/aspectj/JtaTransactionAspectsTests.java b/spring-aspects/src/test/java/org/springframework/transaction/aspectj/JtaTransactionAspectsTests.java index aa478ba43c49..e0bd918a72ea 100644 --- a/spring-aspects/src/test/java/org/springframework/transaction/aspectj/JtaTransactionAspectsTests.java +++ b/spring-aspects/src/test/java/org/springframework/transaction/aspectj/JtaTransactionAspectsTests.java @@ -27,7 +27,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import org.springframework.tests.transaction.CallCountingTransactionManager; +import org.springframework.transaction.testfixture.CallCountingTransactionManager; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; diff --git a/spring-aspects/src/test/java/org/springframework/transaction/aspectj/TransactionAspectTests.java b/spring-aspects/src/test/java/org/springframework/transaction/aspectj/TransactionAspectTests.java index 978b2e2da700..722c357d20e2 100644 --- a/spring-aspects/src/test/java/org/springframework/transaction/aspectj/TransactionAspectTests.java +++ b/spring-aspects/src/test/java/org/springframework/transaction/aspectj/TransactionAspectTests.java @@ -19,7 +19,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.tests.transaction.CallCountingTransactionManager; +import org.springframework.transaction.testfixture.CallCountingTransactionManager; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; diff --git a/spring-aspects/src/test/resources/org/springframework/cache/config/annotation-cache-aspectj.xml b/spring-aspects/src/test/resources/org/springframework/cache/config/annotation-cache-aspectj.xml index 1ff47150738c..1a283f6f6d83 100644 --- a/spring-aspects/src/test/resources/org/springframework/cache/config/annotation-cache-aspectj.xml +++ b/spring-aspects/src/test/resources/org/springframework/cache/config/annotation-cache-aspectj.xml @@ -22,7 +22,7 @@ - + @@ -40,7 +40,7 @@ - + diff --git a/spring-beans/spring-beans.gradle b/spring-beans/spring-beans.gradle index d19807870b50..db894e8b83bc 100644 --- a/spring-beans/spring-beans.gradle +++ b/spring-beans/spring-beans.gradle @@ -10,7 +10,10 @@ dependencies { optional("org.codehaus.groovy:groovy-xml") optional("org.jetbrains.kotlin:kotlin-reflect") optional("org.jetbrains.kotlin:kotlin-stdlib") - testCompile("org.apache.tomcat.embed:tomcat-embed-core") + testCompile(testFixtures(project(":spring-core"))) + testCompile("javax.annotation:javax.annotation-api") + testFixturesApi("org.junit.jupiter:junit-jupiter-api") + testFixturesImplementation("org.assertj:assertj-core") } // This module does joint compilation for Java and Groovy code with the compileGroovy task. @@ -20,13 +23,16 @@ sourceSets { } compileGroovy { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + options.compilerArgs += "-Werror" } // This module also builds Kotlin code and the compileKotlin task naturally depends on // compileJava. We need to redefine dependencies to break task cycles. -def deps = compileGroovy.taskDependencies.immutableValues + compileGroovy.taskDependencies.mutableValues -compileGroovy.dependsOn = deps - "compileJava" -compileKotlin.dependsOn(compileGroovy) -compileKotlin.classpath += files(compileGroovy.destinationDir) +tasks.named('compileGroovy') { + // Groovy only needs the declared dependencies (and not the result of Java compilation) + classpath = sourceSets.main.compileClasspath +} +tasks.named('compileKotlin') { + // Kotlin also depends on the result of Groovy compilation + classpath += files(sourceSets.main.groovy.classesDirectory) +} diff --git a/spring-beans/src/jmh/java/org/springframework/beans/AbstractPropertyAccessorBenchmark.java b/spring-beans/src/jmh/java/org/springframework/beans/AbstractPropertyAccessorBenchmark.java new file mode 100644 index 000000000000..d407318f103c --- /dev/null +++ b/spring-beans/src/jmh/java/org/springframework/beans/AbstractPropertyAccessorBenchmark.java @@ -0,0 +1,101 @@ +/* + * 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.beans; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +import org.springframework.beans.propertyeditors.CustomNumberEditor; +import org.springframework.beans.propertyeditors.StringTrimmerEditor; + +/** + * Benchmark for {@link AbstractPropertyAccessor} use on beans. + * + * @author Brian Clozel + */ +@BenchmarkMode(Mode.Throughput) +public class AbstractPropertyAccessorBenchmark { + + @State(Scope.Benchmark) + public static class BenchmarkState { + + @Param({"DirectFieldAccessor", "BeanWrapper"}) + public String accessor; + + @Param({"none", "stringTrimmer", "numberOnPath", "numberOnNestedPath", "numberOnType"}) + public String customEditor; + + public int[] input; + + public PrimitiveArrayBean target; + + public AbstractPropertyAccessor propertyAccessor; + + @Setup + public void setup() { + this.target = new PrimitiveArrayBean(); + this.input = new int[1024]; + if (this.accessor.equals("DirectFieldAccessor")) { + this.propertyAccessor = new DirectFieldAccessor(this.target); + } + else { + this.propertyAccessor = new BeanWrapperImpl(this.target); + } + switch (this.customEditor) { + case "stringTrimmer": + this.propertyAccessor.registerCustomEditor(String.class, new StringTrimmerEditor(false)); + break; + case "numberOnPath": + this.propertyAccessor.registerCustomEditor(int.class, "array.somePath", new CustomNumberEditor(Integer.class, false)); + break; + case "numberOnNestedPath": + this.propertyAccessor.registerCustomEditor(int.class, "array[0].somePath", new CustomNumberEditor(Integer.class, false)); + break; + case "numberOnType": + this.propertyAccessor.registerCustomEditor(int.class, new CustomNumberEditor(Integer.class, false)); + break; + } + + } + + } + + @Benchmark + public PrimitiveArrayBean setPropertyValue(BenchmarkState state) { + state.propertyAccessor.setPropertyValue("array", state.input); + return state.target; + } + + @SuppressWarnings("unused") + private static class PrimitiveArrayBean { + + private int[] array; + + public int[] getArray() { + return this.array; + } + + public void setArray(int[] array) { + this.array = array; + } + } +} diff --git a/spring-beans/src/jmh/java/org/springframework/beans/factory/ConcurrentBeanFactoryBenchmark.java b/spring-beans/src/jmh/java/org/springframework/beans/factory/ConcurrentBeanFactoryBenchmark.java new file mode 100644 index 000000000000..3214290d1870 --- /dev/null +++ b/spring-beans/src/jmh/java/org/springframework/beans/factory/ConcurrentBeanFactoryBenchmark.java @@ -0,0 +1,83 @@ +/* + * 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.beans.factory; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.infra.Blackhole; + +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.beans.propertyeditors.CustomDateEditor; + +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; + +/** + * Benchmark for creating prototype beans in a concurrent fashion. + * This benchmark requires to customize the number of worker threads {@code -t } on the + * CLI when running this particular benchmark to leverage concurrency. + * + * @author Brian Clozel + */ +@BenchmarkMode(Mode.Throughput) +public class ConcurrentBeanFactoryBenchmark { + + @State(Scope.Benchmark) + public static class BenchmarkState { + + public DefaultListableBeanFactory factory; + + @Setup + public void setup() { + this.factory = new DefaultListableBeanFactory(); + new XmlBeanDefinitionReader(this.factory).loadBeanDefinitions( + qualifiedResource(ConcurrentBeanFactoryBenchmark.class, "context.xml")); + + this.factory.addPropertyEditorRegistrar( + registry -> registry.registerCustomEditor(Date.class, + new CustomDateEditor(new SimpleDateFormat("yyyy/MM/dd"), false))); + } + + } + + @Benchmark + public void concurrentBeanCreation(BenchmarkState state, Blackhole bh) { + bh.consume(state.factory.getBean("bean1")); + bh.consume(state.factory.getBean("bean2")); + } + + + public static class ConcurrentBean { + + private Date date; + + public Date getDate() { + return this.date; + } + + public void setDate(Date date) { + this.date = date; + } + } +} diff --git a/spring-beans/src/jmh/java/org/springframework/beans/factory/DefaultListableBeanFactoryBenchmark.java b/spring-beans/src/jmh/java/org/springframework/beans/factory/DefaultListableBeanFactoryBenchmark.java new file mode 100644 index 000000000000..ec828fb185b6 --- /dev/null +++ b/spring-beans/src/jmh/java/org/springframework/beans/factory/DefaultListableBeanFactoryBenchmark.java @@ -0,0 +1,142 @@ +/* + * 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.beans.factory; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.testfixture.beans.LifecycleBean; +import org.springframework.beans.testfixture.beans.TestBean; + +/** + * Benchmark for retrieving various bean types from the {@link DefaultListableBeanFactory}. + * + * @author Brian Clozel + */ +@BenchmarkMode(Mode.Throughput) +public class DefaultListableBeanFactoryBenchmark { + + public static class Shared { + public DefaultListableBeanFactory beanFactory; + } + + @State(Scope.Benchmark) + public static class PrototypeCreationState extends Shared { + + @Param({"simple", "dependencyCheck", "constructor", "constructorArgument", "properties", "resolvedProperties"}) + public String mode; + + @Setup + public void setup() { + this.beanFactory = new DefaultListableBeanFactory(); + RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); + + switch (this.mode) { + case "simple": + break; + case "dependencyCheck": + rbd = new RootBeanDefinition(LifecycleBean.class); + rbd.setDependencyCheck(RootBeanDefinition.DEPENDENCY_CHECK_OBJECTS); + this.beanFactory.addBeanPostProcessor(new LifecycleBean.PostProcessor()); + break; + case "constructor": + rbd.getConstructorArgumentValues().addGenericArgumentValue("juergen"); + rbd.getConstructorArgumentValues().addGenericArgumentValue("99"); + break; + case "constructorArgument": + rbd.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference("spouse")); + this.beanFactory.registerBeanDefinition("test", rbd); + this.beanFactory.registerBeanDefinition("spouse", new RootBeanDefinition(TestBean.class)); + break; + case "properties": + rbd.getPropertyValues().add("name", "juergen"); + rbd.getPropertyValues().add("age", "99"); + break; + case "resolvedProperties": + rbd.getPropertyValues().add("spouse", new RuntimeBeanReference("spouse")); + this.beanFactory.registerBeanDefinition("spouse", new RootBeanDefinition(TestBean.class)); + break; + } + rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE); + this.beanFactory.registerBeanDefinition("test", rbd); + this.beanFactory.freezeConfiguration(); + } + + } + + @Benchmark + public Object prototypeCreation(PrototypeCreationState state) { + return state.beanFactory.getBean("test"); + } + + @State(Scope.Benchmark) + public static class SingletonLookupState extends Shared { + + @Setup + public void setup() { + this.beanFactory = new DefaultListableBeanFactory(); + this.beanFactory.registerBeanDefinition("test", new RootBeanDefinition(TestBean.class)); + this.beanFactory.freezeConfiguration(); + } + } + + @Benchmark + public Object singletLookup(SingletonLookupState state) { + return state.beanFactory.getBean("test"); + } + + @Benchmark + public Object singletLookupByType(SingletonLookupState state) { + return state.beanFactory.getBean(TestBean.class); + } + + @State(Scope.Benchmark) + public static class SingletonLookupManyBeansState extends Shared { + + @Setup + public void setup() { + this.beanFactory = new DefaultListableBeanFactory(); + this.beanFactory.registerBeanDefinition("test", new RootBeanDefinition(TestBean.class)); + for (int i = 0; i < 1000; i++) { + this.beanFactory.registerBeanDefinition("a" + i, new RootBeanDefinition(A.class)); + } + this.beanFactory.freezeConfiguration(); + } + } + + // See SPR-6870 + @Benchmark + public Object singletLookupByTypeManyBeans(SingletonLookupState state) { + return state.beanFactory.getBean(B.class); + } + + static class A { + } + + static class B { + } + +} diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/ConcurrentBeanFactoryTests-context.xml b/spring-beans/src/jmh/resources/org/springframework/beans/factory/ConcurrentBeanFactoryBenchmark-context.xml similarity index 50% rename from spring-beans/src/test/resources/org/springframework/beans/factory/ConcurrentBeanFactoryTests-context.xml rename to spring-beans/src/jmh/resources/org/springframework/beans/factory/ConcurrentBeanFactoryBenchmark-context.xml index 8dceadf07278..d9336a02b9ee 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/ConcurrentBeanFactoryTests-context.xml +++ b/spring-beans/src/jmh/resources/org/springframework/beans/factory/ConcurrentBeanFactoryBenchmark-context.xml @@ -1,14 +1,13 @@ - + - - - - diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java index e079a489002e..b56ec6b9533b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -115,7 +115,7 @@ protected AbstractNestablePropertyAccessor(boolean registerDefaultEditors) { /** * Create a new accessor for the given object. - * @param object object wrapped by this accessor + * @param object the object wrapped by this accessor */ protected AbstractNestablePropertyAccessor(Object object) { registerDefaultEditors(); @@ -134,7 +134,7 @@ protected AbstractNestablePropertyAccessor(Class clazz) { /** * Create a new accessor for the given object, * registering a nested path that the object is in. - * @param object object wrapped by this accessor + * @param object the object wrapped by this accessor * @param nestedPath the nested path of the object * @param rootObject the root object at the top of the path */ @@ -146,7 +146,7 @@ protected AbstractNestablePropertyAccessor(Object object, String nestedPath, Obj /** * Create a new accessor for the given object, * registering a nested path that the object is in. - * @param object object wrapped by this accessor + * @param object the object wrapped by this accessor * @param nestedPath the nested path of the object * @param parent the containing accessor (must not be {@code null}) */ @@ -305,8 +305,10 @@ private void processKeyedProperty(PropertyTokenHolder tokens, PropertyValue pv) Class componentType = propValue.getClass().getComponentType(); Object newArray = Array.newInstance(componentType, arrayIndex + 1); System.arraycopy(propValue, 0, newArray, 0, length); - setPropertyValue(tokens.actualName, newArray); - propValue = getPropertyValue(tokens.actualName); + int lastKeyIndex = tokens.canonicalName.lastIndexOf('['); + String propName = tokens.canonicalName.substring(0, lastKeyIndex); + setPropertyValue(propName, newArray); + propValue = getPropertyValue(propName); } Array.set(propValue, arrayIndex, convertedValue); } @@ -422,9 +424,12 @@ private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) } return; } - else { - throw createNotWritablePropertyException(tokens.canonicalName); + if (this.suppressNotWritablePropertyException) { + // Optimization for common ignoreUnknown=true scenario since the + // exception would be caught and swallowed higher up anyway... + return; } + throw createNotWritablePropertyException(tokens.canonicalName); } Object oldValue = null; @@ -737,7 +742,7 @@ protected PropertyHandler getPropertyHandler(String propertyName) throws BeansEx /** * Create a new nested property accessor instance. * Can be overridden in subclasses to create a PropertyAccessor subclass. - * @param object object wrapped by this PropertyAccessor + * @param object the object wrapped by this PropertyAccessor * @param nestedPath the nested path of the object * @return the nested PropertyAccessor instance */ @@ -806,7 +811,6 @@ protected String getFinalPath(AbstractNestablePropertyAccessor pa, String nested * @param propertyPath property path, which may be nested * @return a property accessor for the target bean */ - @SuppressWarnings("unchecked") // avoid nested generic protected AbstractNestablePropertyAccessor getPropertyAccessorForPropertyPath(String propertyPath) { int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath); // Handle nested properties recursively. diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java index cd2e7e516042..1d6b5f48eab9 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,6 +40,8 @@ public abstract class AbstractPropertyAccessor extends TypeConverterSupport impl private boolean autoGrowNestedPaths = false; + boolean suppressNotWritablePropertyException = false; + @Override public void setExtractOldValueForEditor(boolean extractOldValueForEditor) { @@ -89,30 +91,41 @@ public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean List propertyAccessExceptions = null; List propertyValues = (pvs instanceof MutablePropertyValues ? ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues())); - for (PropertyValue pv : propertyValues) { - try { - // This method may throw any BeansException, which won't be caught + + if (ignoreUnknown) { + this.suppressNotWritablePropertyException = true; + } + try { + for (PropertyValue pv : propertyValues) { + // setPropertyValue may throw any BeansException, which won't be caught // here, if there is a critical failure such as no matching field. // We can attempt to deal only with less serious exceptions. - setPropertyValue(pv); - } - catch (NotWritablePropertyException ex) { - if (!ignoreUnknown) { - throw ex; + try { + setPropertyValue(pv); } - // Otherwise, just ignore it and continue... - } - catch (NullValueInNestedPathException ex) { - if (!ignoreInvalid) { - throw ex; + catch (NotWritablePropertyException ex) { + if (!ignoreUnknown) { + throw ex; + } + // Otherwise, just ignore it and continue... } - // Otherwise, just ignore it and continue... - } - catch (PropertyAccessException ex) { - if (propertyAccessExceptions == null) { - propertyAccessExceptions = new ArrayList<>(); + catch (NullValueInNestedPathException ex) { + if (!ignoreInvalid) { + throw ex; + } + // Otherwise, just ignore it and continue... + } + catch (PropertyAccessException ex) { + if (propertyAccessExceptions == null) { + propertyAccessExceptions = new ArrayList<>(); + } + propertyAccessExceptions.add(ex); } - propertyAccessExceptions.add(ex); + } + } + finally { + if (ignoreUnknown) { + this.suppressNotWritablePropertyException = false; } } diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java index 3e0ef30ecd98..053393e6c438 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java @@ -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,6 +16,7 @@ package org.springframework.beans; +import java.beans.ConstructorProperties; import java.beans.PropertyDescriptor; import java.beans.PropertyEditor; import java.lang.reflect.Constructor; @@ -38,15 +39,20 @@ import kotlin.reflect.KFunction; import kotlin.reflect.KParameter; import kotlin.reflect.full.KClasses; +import kotlin.reflect.jvm.KCallablesJvm; import kotlin.reflect.jvm.ReflectJvmMapping; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.KotlinDetector; import org.springframework.core.MethodParameter; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.CollectionUtils; import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; @@ -55,8 +61,11 @@ * Static convenience methods for JavaBeans: for instantiating beans, * checking bean property types, copying bean properties, etc. * - *

Mainly for use within the framework, but to some degree also - * useful for application classes. + *

Mainly for internal use within the framework, but to some degree also + * useful for application classes. Consider + * Apache Commons BeanUtils, + * BULL - Bean Utils Light Library, + * or similar third-party frameworks for more comprehensive bean utilities. * * @author Rod Johnson * @author Juergen Hoeller @@ -68,6 +77,9 @@ public abstract class BeanUtils { private static final Log logger = LogFactory.getLog(BeanUtils.class); + private static final ParameterNameDiscoverer parameterNameDiscoverer = + new DefaultParameterNameDiscoverer(); + private static final Set> unknownEditorTypes = Collections.newSetFromMap(new ConcurrentReferenceHashMap<>(64)); @@ -214,6 +226,35 @@ public static T instantiateClass(Constructor ctor, Object... args) throws } } + /** + * Return a resolvable constructor for the provided class, either a primary constructor + * or single public constructor or simply a default constructor. Callers have to be + * prepared to resolve arguments for the returned constructor's parameters, if any. + * @param clazz the class to check + * @since 5.3 + * @see #findPrimaryConstructor + */ + @SuppressWarnings("unchecked") + public static Constructor getResolvableConstructor(Class clazz) { + Constructor ctor = findPrimaryConstructor(clazz); + if (ctor == null) { + Constructor[] ctors = clazz.getConstructors(); + if (ctors.length == 1) { + ctor = (Constructor) ctors[0]; + } + else { + try { + ctor = clazz.getDeclaredConstructor(); + } + catch (NoSuchMethodException ex) { + throw new IllegalStateException("No primary or single public constructor found for " + + clazz + " - and no default constructor found either"); + } + } + } + return ctor; + } + /** * Return the primary constructor of the provided class. For Kotlin classes, this * returns the Java constructor corresponding to the Kotlin primary constructor @@ -223,15 +264,11 @@ public static T instantiateClass(Constructor ctor, Object... args) throws * @since 5.0 * @see Kotlin docs */ - @SuppressWarnings("unchecked") @Nullable public static Constructor findPrimaryConstructor(Class clazz) { Assert.notNull(clazz, "Class must not be null"); if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(clazz)) { - Constructor kotlinPrimaryConstructor = KotlinDelegate.findPrimaryConstructor(clazz); - if (kotlinPrimaryConstructor != null) { - return kotlinPrimaryConstructor; - } + return KotlinDelegate.findPrimaryConstructor(clazz); } return null; } @@ -438,8 +475,7 @@ else if (startParen == -1) { * @throws BeansException if PropertyDescriptor look fails */ public static PropertyDescriptor[] getPropertyDescriptors(Class clazz) throws BeansException { - CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz); - return cr.getPropertyDescriptors(); + return CachedIntrospectionResults.forClass(clazz).getPropertyDescriptors(); } /** @@ -450,11 +486,8 @@ public static PropertyDescriptor[] getPropertyDescriptors(Class clazz) throws * @throws BeansException if PropertyDescriptor lookup fails */ @Nullable - public static PropertyDescriptor getPropertyDescriptor(Class clazz, String propertyName) - throws BeansException { - - CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz); - return cr.getPropertyDescriptor(propertyName); + public static PropertyDescriptor getPropertyDescriptor(Class clazz, String propertyName) throws BeansException { + return CachedIntrospectionResults.forClass(clazz).getPropertyDescriptor(propertyName); } /** @@ -507,6 +540,7 @@ public static PropertyEditor findEditorByConvention(@Nullable Class targetTyp if (targetType == null || targetType.isArray() || unknownEditorTypes.contains(targetType)) { return null; } + ClassLoader cl = targetType.getClassLoader(); if (cl == null) { try { @@ -523,27 +557,34 @@ public static PropertyEditor findEditorByConvention(@Nullable Class targetTyp return null; } } - String editorName = targetType.getName() + "Editor"; + + String targetTypeName = targetType.getName(); + String editorName = targetTypeName + "Editor"; try { Class editorClass = cl.loadClass(editorName); - if (!PropertyEditor.class.isAssignableFrom(editorClass)) { - if (logger.isInfoEnabled()) { - logger.info("Editor class [" + editorName + - "] does not implement [java.beans.PropertyEditor] interface"); + if (editorClass != null) { + if (!PropertyEditor.class.isAssignableFrom(editorClass)) { + if (logger.isInfoEnabled()) { + logger.info("Editor class [" + editorName + + "] does not implement [java.beans.PropertyEditor] interface"); + } + unknownEditorTypes.add(targetType); + return null; } - unknownEditorTypes.add(targetType); - return null; + return (PropertyEditor) instantiateClass(editorClass); } - return (PropertyEditor) instantiateClass(editorClass); + // Misbehaving ClassLoader returned null instead of ClassNotFoundException + // - fall back to unknown editor type registration below } catch (ClassNotFoundException ex) { - if (logger.isTraceEnabled()) { - logger.trace("No property editor [" + editorName + "] found for type " + - targetType.getName() + " according to 'Editor' suffix convention"); - } - unknownEditorTypes.add(targetType); - return null; + // Ignore - fall back to unknown editor type registration below } + if (logger.isTraceEnabled()) { + logger.trace("No property editor [" + editorName + "] found for type " + + targetTypeName + " according to 'Editor' suffix convention"); + } + unknownEditorTypes.add(targetType); + return null; } /** @@ -582,6 +623,26 @@ public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd) { } } + /** + * Determine required parameter names for the given constructor, + * considering the JavaBeans {@link ConstructorProperties} annotation + * as well as Spring's {@link DefaultParameterNameDiscoverer}. + * @param ctor the constructor to find parameter names for + * @return the parameter names (matching the constructor's parameter count) + * @throws IllegalStateException if the parameter names are not resolvable + * @since 5.3 + * @see ConstructorProperties + * @see DefaultParameterNameDiscoverer + */ + public static String[] getParameterNames(Constructor ctor) { + ConstructorProperties cp = ctor.getAnnotation(ConstructorProperties.class); + String[] paramNames = (cp != null ? cp.value() : parameterNameDiscoverer.getParameterNames(ctor)); + Assert.state(paramNames != null, () -> "Cannot resolve parameter names for constructor " + ctor); + Assert.state(paramNames.length == ctor.getParameterCount(), + () -> "Invalid number of parameter names: " + paramNames.length + " for constructor " + ctor); + return paramNames; + } + /** * Check if the given type represents a "simple" property: a simple value * type or an array of simple value types. @@ -680,6 +741,8 @@ public static void copyProperties(Object source, Object target, String... ignore *

Note: The source and target classes do not have to match or even be derived * from each other, as long as the properties match. Any bean properties that the * source bean exposes but the target bean does not will silently be ignored. + *

As of Spring Framework 5.3, this method honors generic type information + * when matching properties in the source and target objects. * @param source the source bean * @param target the target bean * @param editable the class (or interface) to restrict property setting to @@ -710,21 +773,31 @@ private static void copyProperties(Object source, Object target, @Nullable Class PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); if (sourcePd != null) { Method readMethod = sourcePd.getReadMethod(); - if (readMethod != null && - ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) { - try { - if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { - readMethod.setAccessible(true); + if (readMethod != null) { + ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod); + ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0); + + // Ignore generic types in assignable check if either ResolvableType has unresolvable generics. + boolean isAssignable = + (sourceResolvableType.hasUnresolvableGenerics() || targetResolvableType.hasUnresolvableGenerics() ? + ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) : + targetResolvableType.isAssignableFrom(sourceResolvableType)); + + if (isAssignable) { + try { + if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { + readMethod.setAccessible(true); + } + Object value = readMethod.invoke(source); + if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { + writeMethod.setAccessible(true); + } + writeMethod.invoke(target, value); } - Object value = readMethod.invoke(source); - if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { - writeMethod.setAccessible(true); + catch (Throwable ex) { + throw new FatalBeanException( + "Could not copy property '" + targetPd.getName() + "' from source to target", ex); } - writeMethod.invoke(target, value); - } - catch (Throwable ex) { - throw new FatalBeanException( - "Could not copy property '" + targetPd.getName() + "' from source to target", ex); } } } @@ -776,8 +849,13 @@ public static T instantiateClass(Constructor ctor, Object... args) if (kotlinConstructor == null) { return ctor.newInstance(args); } + + if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers()))) { + KCallablesJvm.setAccessible(kotlinConstructor, true); + } + List parameters = kotlinConstructor.getParameters(); - Map argParameters = new HashMap<>(parameters.size()); + Map argParameters = CollectionUtils.newHashMap(parameters.size()); Assert.isTrue(args.length <= parameters.size(), "Number of provided arguments should be less of equals than number of constructor parameters"); for (int i = 0 ; i < args.length ; i++) { diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java index 4baa57cb7bdb..c2fb1aca6f71 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java @@ -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. @@ -97,7 +97,7 @@ public BeanWrapperImpl(boolean registerDefaultEditors) { /** * Create a new BeanWrapperImpl for the given object. - * @param object object wrapped by this BeanWrapper + * @param object the object wrapped by this BeanWrapper */ public BeanWrapperImpl(Object object) { super(object); @@ -114,7 +114,7 @@ public BeanWrapperImpl(Class clazz) { /** * Create a new BeanWrapperImpl for the given object, * registering a nested path that the object is in. - * @param object object wrapped by this BeanWrapper + * @param object the object wrapped by this BeanWrapper * @param nestedPath the nested path of the object * @param rootObject the root object at the top of the path */ @@ -125,7 +125,7 @@ public BeanWrapperImpl(Object object, String nestedPath, Object rootObject) { /** * Create a new BeanWrapperImpl for the given object, * registering a nested path that the object is in. - * @param object object wrapped by this BeanWrapper + * @param object the object wrapped by this BeanWrapper * @param nestedPath the nested path of the object * @param parent the containing BeanWrapper (must not be {@code null}) */ @@ -289,15 +289,15 @@ public TypeDescriptor nested(int level) { @Override @Nullable public Object getValue() throws Exception { - final Method readMethod = this.pd.getReadMethod(); + Method readMethod = this.pd.getReadMethod(); if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction) () -> { ReflectionUtils.makeAccessible(readMethod); return null; }); try { - return AccessController.doPrivileged((PrivilegedExceptionAction) () -> - readMethod.invoke(getWrappedInstance(), (Object[]) null), acc); + return AccessController.doPrivileged((PrivilegedExceptionAction) + () -> readMethod.invoke(getWrappedInstance(), (Object[]) null), acc); } catch (PrivilegedActionException pae) { throw pae.getException(); @@ -310,8 +310,8 @@ public Object getValue() throws Exception { } @Override - public void setValue(final @Nullable Object value) throws Exception { - final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ? + public void setValue(@Nullable Object value) throws Exception { + Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ? ((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() : this.pd.getWriteMethod()); if (System.getSecurityManager() != null) { @@ -320,8 +320,8 @@ public void setValue(final @Nullable Object value) throws Exception { return null; }); try { - AccessController.doPrivileged((PrivilegedExceptionAction) () -> - writeMethod.invoke(getWrappedInstance(), value), acc); + AccessController.doPrivileged((PrivilegedExceptionAction) + () -> writeMethod.invoke(getWrappedInstance(), value), acc); } catch (PrivilegedActionException ex) { throw ex.getException(); diff --git a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java index ca14d6995d20..7b7a67d91ccf 100644 --- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java +++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java @@ -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. @@ -20,7 +20,10 @@ import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -43,9 +46,9 @@ * Internal class that caches JavaBeans {@link java.beans.PropertyDescriptor} * information for a Java class. Not intended for direct use by application code. * - *

Necessary for own caching of descriptors within the application's - * ClassLoader, rather than rely on the JDK's system-wide BeanInfo cache - * (in order to avoid leaks on ClassLoader shutdown). + *

Necessary for Spring's own caching of bean descriptors within the application + * {@link ClassLoader}, rather than relying on the JDK's system-wide {@link BeanInfo} + * cache (in order to avoid leaks on individual application shutdown in a shared JVM). * *

Information is cached statically, so we don't need to create new * objects of this class for every JavaBean we manipulate. Hence, this class @@ -92,12 +95,14 @@ public final class CachedIntrospectionResults { */ public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore"; + private static final PropertyDescriptor[] EMPTY_PROPERTY_DESCRIPTOR_ARRAY = {}; + private static final boolean shouldIntrospectorIgnoreBeaninfoClasses = SpringProperties.getFlag(IGNORE_BEANINFO_PROPERTY_NAME); /** Stores the BeanInfoFactory instances. */ - private static List beanInfoFactories = SpringFactoriesLoader.loadFactories( + private static final List beanInfoFactories = SpringFactoriesLoader.loadFactories( BeanInfoFactory.class, CachedIntrospectionResults.class.getClassLoader()); private static final Log logger = LogFactory.getLog(CachedIntrospectionResults.class); @@ -163,7 +168,6 @@ public static void clearClassLoader(@Nullable ClassLoader classLoader) { * @return the corresponding CachedIntrospectionResults * @throws BeansException in case of introspection failure */ - @SuppressWarnings("unchecked") static CachedIntrospectionResults forClass(Class beanClass) throws BeansException { CachedIntrospectionResults results = strongClassCache.get(beanClass); if (results != null) { @@ -254,7 +258,7 @@ private static BeanInfo getBeanInfo(Class beanClass) throws IntrospectionExce private final BeanInfo beanInfo; /** PropertyDescriptor objects keyed by property name String. */ - private final Map propertyDescriptorCache; + private final Map propertyDescriptors; /** TypeDescriptor objects keyed by PropertyDescriptor. */ private final ConcurrentMap typeDescriptorCache; @@ -275,7 +279,9 @@ private CachedIntrospectionResults(Class beanClass) throws BeansException { if (logger.isTraceEnabled()) { logger.trace("Caching PropertyDescriptors for class [" + beanClass.getName() + "]"); } - this.propertyDescriptorCache = new LinkedHashMap<>(); + this.propertyDescriptors = new LinkedHashMap<>(); + + Set readMethodNames = new HashSet<>(); // This call is slow so we do it once. PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors(); @@ -292,17 +298,26 @@ private CachedIntrospectionResults(Class beanClass) throws BeansException { "; editor [" + pd.getPropertyEditorClass().getName() + "]" : "")); } pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd); - this.propertyDescriptorCache.put(pd.getName(), pd); + this.propertyDescriptors.put(pd.getName(), pd); + Method readMethod = pd.getReadMethod(); + if (readMethod != null) { + readMethodNames.add(readMethod.getName()); + } } // Explicitly check implemented interfaces for setter/getter methods as well, // in particular for Java 8 default methods... Class currClass = beanClass; while (currClass != null && currClass != Object.class) { - introspectInterfaces(beanClass, currClass); + introspectInterfaces(beanClass, currClass, readMethodNames); currClass = currClass.getSuperclass(); } + // Check for record-style accessors without prefix: e.g. "lastName()" + // - accessor method directly referring to instance field of same name + // - same convention for component accessors of Java 15 record classes + introspectPlainAccessors(beanClass, readMethodNames); + this.typeDescriptorCache = new ConcurrentReferenceHashMap<>(); } catch (IntrospectionException ex) { @@ -310,24 +325,58 @@ private CachedIntrospectionResults(Class beanClass) throws BeansException { } } - private void introspectInterfaces(Class beanClass, Class currClass) throws IntrospectionException { + private void introspectInterfaces(Class beanClass, Class currClass, Set readMethodNames) + throws IntrospectionException { + for (Class ifc : currClass.getInterfaces()) { if (!ClassUtils.isJavaLanguageInterface(ifc)) { for (PropertyDescriptor pd : getBeanInfo(ifc).getPropertyDescriptors()) { - PropertyDescriptor existingPd = this.propertyDescriptorCache.get(pd.getName()); + PropertyDescriptor existingPd = this.propertyDescriptors.get(pd.getName()); if (existingPd == null || (existingPd.getReadMethod() == null && pd.getReadMethod() != null)) { // GenericTypeAwarePropertyDescriptor leniently resolves a set* write method // against a declared read method, so we prefer read method descriptors here. pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd); - this.propertyDescriptorCache.put(pd.getName(), pd); + this.propertyDescriptors.put(pd.getName(), pd); + Method readMethod = pd.getReadMethod(); + if (readMethod != null) { + readMethodNames.add(readMethod.getName()); + } } } - introspectInterfaces(ifc, ifc); + introspectInterfaces(ifc, ifc, readMethodNames); + } + } + } + + private void introspectPlainAccessors(Class beanClass, Set readMethodNames) + throws IntrospectionException { + + for (Method method : beanClass.getMethods()) { + if (!this.propertyDescriptors.containsKey(method.getName()) && + !readMethodNames.contains((method.getName())) && isPlainAccessor(method)) { + this.propertyDescriptors.put(method.getName(), + new GenericTypeAwarePropertyDescriptor(beanClass, method.getName(), method, null, null)); + readMethodNames.add(method.getName()); } } } + private boolean isPlainAccessor(Method method) { + if (method.getParameterCount() > 0 || method.getReturnType() == void.class || + method.getDeclaringClass() == Object.class || Modifier.isStatic(method.getModifiers())) { + return false; + } + try { + // Accessor method referring to instance field of same name? + method.getDeclaringClass().getDeclaredField(method.getName()); + return true; + } + catch (Exception ex) { + return false; + } + } + BeanInfo getBeanInfo() { return this.beanInfo; @@ -339,27 +388,19 @@ Class getBeanClass() { @Nullable PropertyDescriptor getPropertyDescriptor(String name) { - PropertyDescriptor pd = this.propertyDescriptorCache.get(name); + PropertyDescriptor pd = this.propertyDescriptors.get(name); if (pd == null && StringUtils.hasLength(name)) { // Same lenient fallback checking as in Property... - pd = this.propertyDescriptorCache.get(StringUtils.uncapitalize(name)); + pd = this.propertyDescriptors.get(StringUtils.uncapitalize(name)); if (pd == null) { - pd = this.propertyDescriptorCache.get(StringUtils.capitalize(name)); + pd = this.propertyDescriptors.get(StringUtils.capitalize(name)); } } - return (pd == null || pd instanceof GenericTypeAwarePropertyDescriptor ? pd : - buildGenericTypeAwarePropertyDescriptor(getBeanClass(), pd)); + return pd; } PropertyDescriptor[] getPropertyDescriptors() { - PropertyDescriptor[] pds = new PropertyDescriptor[this.propertyDescriptorCache.size()]; - int i = 0; - for (PropertyDescriptor pd : this.propertyDescriptorCache.values()) { - pds[i] = (pd instanceof GenericTypeAwarePropertyDescriptor ? pd : - buildGenericTypeAwarePropertyDescriptor(getBeanClass(), pd)); - i++; - } - return pds; + return this.propertyDescriptors.values().toArray(EMPTY_PROPERTY_DESCRIPTOR_ARRAY); } private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class beanClass, PropertyDescriptor pd) { diff --git a/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java b/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java index f526ab3cdcf1..a98c6eb41b03 100644 --- a/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java @@ -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. @@ -52,7 +52,7 @@ public class DirectFieldAccessor extends AbstractNestablePropertyAccessor { /** * Create a new DirectFieldAccessor for the given object. - * @param object object wrapped by this DirectFieldAccessor + * @param object the object wrapped by this DirectFieldAccessor */ public DirectFieldAccessor(Object object) { super(object); @@ -61,7 +61,7 @@ public DirectFieldAccessor(Object object) { /** * Create a new DirectFieldAccessor for the given object, * registering a nested path that the object is in. - * @param object object wrapped by this DirectFieldAccessor + * @param object the object wrapped by this DirectFieldAccessor * @param nestedPath the nested path of the object * @param parent the containing DirectFieldAccessor (must not be {@code null}) */ @@ -92,8 +92,7 @@ protected DirectFieldAccessor newNestedPropertyAccessor(Object object, String ne @Override protected NotWritablePropertyException createNotWritablePropertyException(String propertyName) { PropertyMatches matches = PropertyMatches.forField(propertyName, getRootClass()); - throw new NotWritablePropertyException( - getRootClass(), getNestedPath() + propertyName, + throw new NotWritablePropertyException(getRootClass(), getNestedPath() + propertyName, matches.buildErrorMessage(), matches.getPossibleMatches()); } diff --git a/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java b/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java index e8952301f28f..21ce57cf6add 100644 --- a/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java +++ b/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,8 +41,8 @@ /** * Decorator for a standard {@link BeanInfo} object, e.g. as created by - * {@link Introspector#getBeanInfo(Class)}, designed to discover and register static - * and/or non-void returning setter methods. For example: + * {@link Introspector#getBeanInfo(Class)}, designed to discover and register + * static and/or non-void returning setter methods. For example: * *

  * public class Bean {
@@ -145,11 +145,10 @@ private List findCandidateWriteMethods(MethodDescriptor[] methodDescript
 
 	public static boolean isCandidateWriteMethod(Method method) {
 		String methodName = method.getName();
-		Class[] parameterTypes = method.getParameterTypes();
-		int nParams = parameterTypes.length;
+		int nParams = method.getParameterCount();
 		return (methodName.length() > 3 && methodName.startsWith("set") && Modifier.isPublic(method.getModifiers()) &&
 				(!void.class.isAssignableFrom(method.getReturnType()) || Modifier.isStatic(method.getModifiers())) &&
-				(nParams == 1 || (nParams == 2 && int.class == parameterTypes[0])));
+				(nParams == 1 || (nParams == 2 && int.class == method.getParameterTypes()[0])));
 	}
 
 	private void handleCandidateWriteMethod(Method method) throws IntrospectionException {
@@ -209,7 +208,7 @@ private PropertyDescriptor findExistingPropertyDescriptor(String propertyName, C
 	}
 
 	private String propertyNameFor(Method method) {
-		return Introspector.decapitalize(method.getName().substring(3, method.getName().length()));
+		return Introspector.decapitalize(method.getName().substring(3));
 	}
 
 
@@ -488,7 +487,7 @@ public void setPropertyEditorClass(@Nullable Class propertyEditorClass) {
 		}
 
 		/*
-		 * See java.beans.IndexedPropertyDescriptor#equals(java.lang.Object)
+		 * See java.beans.IndexedPropertyDescriptor#equals
 		 */
 		@Override
 		public boolean equals(@Nullable Object other) {
@@ -535,11 +534,13 @@ static class PropertyDescriptorComparator implements Comparator propertyType;
 
+	@Nullable
 	private final Class propertyEditorClass;
 
 
 	public GenericTypeAwarePropertyDescriptor(Class beanClass, String propertyName,
-			@Nullable Method readMethod, @Nullable Method writeMethod, Class propertyEditorClass)
-			throws IntrospectionException {
+			@Nullable Method readMethod, @Nullable Method writeMethod,
+			@Nullable Class propertyEditorClass) throws IntrospectionException {
 
 		super(propertyName, null, null);
 		this.beanClass = beanClass;
@@ -156,6 +157,7 @@ public Class getPropertyType() {
 	}
 
 	@Override
+	@Nullable
 	public Class getPropertyEditorClass() {
 		return this.propertyEditorClass;
 	}
diff --git a/spring-beans/src/main/java/org/springframework/beans/MutablePropertyValues.java b/spring-beans/src/main/java/org/springframework/beans/MutablePropertyValues.java
index b3f05399f4af..97c0a0ab05b9 100644
--- a/spring-beans/src/main/java/org/springframework/beans/MutablePropertyValues.java
+++ b/spring-beans/src/main/java/org/springframework/beans/MutablePropertyValues.java
@@ -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.
@@ -49,7 +49,7 @@ public class MutablePropertyValues implements PropertyValues, Serializable {
 	@Nullable
 	private Set processedProperties;
 
-	private volatile boolean converted = false;
+	private volatile boolean converted;
 
 
 	/**
diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java b/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java
index c5ac50cb4b42..aa9909822d18 100644
--- a/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java
+++ b/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2019 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -66,8 +66,7 @@ public static Class findPropertyType(@Nullable Method readMethod, @Nullable M
 		Class propertyType = null;
 
 		if (readMethod != null) {
-			Class[] params = readMethod.getParameterTypes();
-			if (params.length != 0) {
+			if (readMethod.getParameterCount() != 0) {
 				throw new IntrospectionException("Bad read method arg count: " + readMethod);
 			}
 			propertyType = readMethod.getReturnType();
diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java b/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java
index e73a34fb7fbe..d1354e1d89b0 100644
--- a/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java
+++ b/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java
@@ -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.
@@ -71,6 +71,7 @@
 import org.springframework.beans.propertyeditors.URLEditor;
 import org.springframework.beans.propertyeditors.UUIDEditor;
 import org.springframework.beans.propertyeditors.ZoneIdEditor;
+import org.springframework.core.SpringProperties;
 import org.springframework.core.convert.ConversionService;
 import org.springframework.core.io.Resource;
 import org.springframework.core.io.support.ResourceArrayPropertyEditor;
@@ -84,6 +85,7 @@
  *
  * @author Juergen Hoeller
  * @author Rob Harrop
+ * @author Sebastien Deleuze
  * @since 1.2.6
  * @see java.beans.PropertyEditorManager
  * @see java.beans.PropertyEditorSupport#setAsText
@@ -91,6 +93,14 @@
  */
 public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
 
+	/**
+	 * Boolean flag controlled by a {@code spring.xml.ignore} system property that instructs Spring to
+	 * ignore XML, i.e. to not initialize the XML-related infrastructure.
+	 * 

The default is "false". + */ + private static final boolean shouldIgnoreXml = SpringProperties.getFlag("spring.xml.ignore"); + + @Nullable private ConversionService conversionService; @@ -208,7 +218,9 @@ private void createDefaultEditors() { this.defaultEditors.put(Currency.class, new CurrencyEditor()); this.defaultEditors.put(File.class, new FileEditor()); this.defaultEditors.put(InputStream.class, new InputStreamEditor()); - this.defaultEditors.put(InputSource.class, new InputSourceEditor()); + if (!shouldIgnoreXml) { + this.defaultEditors.put(InputSource.class, new InputSourceEditor()); + } this.defaultEditors.put(Locale.class, new LocaleEditor()); this.defaultEditors.put(Path.class, new PathEditor()); this.defaultEditors.put(Pattern.class, new PatternEditor()); @@ -501,7 +513,7 @@ private void addStrippedPropertyPaths(List strippedPaths, String nestedP if (endIndex != -1) { String prefix = propertyPath.substring(0, startIndex); String key = propertyPath.substring(startIndex, endIndex + 1); - String suffix = propertyPath.substring(endIndex + 1, propertyPath.length()); + String suffix = propertyPath.substring(endIndex + 1); // Strip the first key. strippedPaths.add(nestedPath + prefix + suffix); // Search for further keys to strip, with the first key stripped. diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyValues.java b/spring-beans/src/main/java/org/springframework/beans/PropertyValues.java index 6840bb9116af..b754a32a0f60 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyValues.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyValues.java @@ -78,7 +78,7 @@ default Stream stream() { /** * Return the changes since the previous PropertyValues. * Subclasses should also override {@code equals}. - * @param old old property values + * @param old the old property values * @return the updated or new properties. * Return empty PropertyValues if there are no changes. * @see Object#equals diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeMismatchException.java b/spring-beans/src/main/java/org/springframework/beans/TypeMismatchException.java index 3177c4b20ed2..4a6f52400e8d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeMismatchException.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeMismatchException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,10 +41,10 @@ public class TypeMismatchException extends PropertyAccessException { private String propertyName; @Nullable - private transient Object value; + private final transient Object value; @Nullable - private Class requiredType; + private final Class requiredType; /** diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java index e0b345f62290..f31c29d85170 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,8 @@ /** * The root interface for accessing a Spring bean container. - * This is the basic client view of a bean container; + * + *

This is the basic client view of a bean container; * further interfaces such as {@link ListableBeanFactory} and * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory} * are available for specific purposes. @@ -130,7 +131,7 @@ public interface BeanFactory { * Singleton or Prototype design pattern. Callers may retain references to * returned objects in the case of Singleton beans. *

Translates aliases back to the corresponding canonical bean name. - * Will ask the parent factory if the bean cannot be found in this factory instance. + *

Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to retrieve * @return an instance of the bean * @throws NoSuchBeanDefinitionException if there is no bean with the specified name @@ -145,7 +146,7 @@ public interface BeanFactory { * required type. This means that ClassCastException can't be thrown on casting * the result correctly, as can happen with {@link #getBean(String)}. *

Translates aliases back to the corresponding canonical bean name. - * Will ask the parent factory if the bean cannot be found in this factory instance. + *

Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to retrieve * @param requiredType type the bean must match; can be an interface or superclass * @return an instance of the bean @@ -258,7 +259,7 @@ public interface BeanFactory { * to a scoped bean as well. Use the {@link #isPrototype} operation to explicitly * check for independent instances. *

Translates aliases back to the corresponding canonical bean name. - * Will ask the parent factory if the bean cannot be found in this factory instance. + *

Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @return whether this bean corresponds to a singleton instance * @throws NoSuchBeanDefinitionException if there is no bean with the given name @@ -275,7 +276,7 @@ public interface BeanFactory { * to a scoped bean as well. Use the {@link #isSingleton} operation to explicitly * check for a shared singleton instance. *

Translates aliases back to the corresponding canonical bean name. - * Will ask the parent factory if the bean cannot be found in this factory instance. + *

Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @return whether this bean will always deliver independent instances * @throws NoSuchBeanDefinitionException if there is no bean with the given name @@ -290,7 +291,7 @@ public interface BeanFactory { * More specifically, check whether a {@link #getBean} call for the given name * would return an object that is assignable to the specified target type. *

Translates aliases back to the corresponding canonical bean name. - * Will ask the parent factory if the bean cannot be found in this factory instance. + *

Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @param typeToMatch the type to match against (as a {@code ResolvableType}) * @return {@code true} if the bean type matches, @@ -307,7 +308,7 @@ public interface BeanFactory { * More specifically, check whether a {@link #getBean} call for the given name * would return an object that is assignable to the specified target type. *

Translates aliases back to the corresponding canonical bean name. - * Will ask the parent factory if the bean cannot be found in this factory instance. + *

Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @param typeToMatch the type to match against (as a {@code Class}) * @return {@code true} if the bean type matches, @@ -326,7 +327,7 @@ public interface BeanFactory { * as exposed by {@link FactoryBean#getObjectType()}. This may lead to the initialization * of a previously uninitialized {@code FactoryBean} (see {@link #getType(String, boolean)}). *

Translates aliases back to the corresponding canonical bean name. - * Will ask the parent factory if the bean cannot be found in this factory instance. + *

Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @return the type of the bean, or {@code null} if not determinable * @throws NoSuchBeanDefinitionException if there is no bean with the given name @@ -345,7 +346,7 @@ public interface BeanFactory { * {@code allowFactoryBeanInit} flag, this may lead to the initialization of a previously * uninitialized {@code FactoryBean} if no early type information is available. *

Translates aliases back to the corresponding canonical bean name. - * Will ask the parent factory if the bean cannot be found in this factory instance. + *

Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @param allowFactoryBeanInit whether a {@code FactoryBean} may get initialized * just for the purpose of determining its object type @@ -360,7 +361,7 @@ public interface BeanFactory { /** * Return the aliases for the given bean name, if any. - * All of those aliases point to the same bean when used in a {@link #getBean} call. + *

All of those aliases point to the same bean when used in a {@link #getBean} call. *

If the given name is an alias, the corresponding original bean name * and other aliases (if any) will be returned, with the original bean name * being the first element in the array. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/FactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/FactoryBean.java index c20df830a433..224563cc7749 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/FactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/FactoryBean.java @@ -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. @@ -39,10 +39,16 @@ * *

{@code FactoryBean} is a programmatic contract. Implementations are not * supposed to rely on annotation-driven injection or other reflective facilities. - * {@link #getObjectType()} {@link #getObject()} invocations may arrive early in - * the bootstrap process, even ahead of any post-processor setup. If you need access + * {@link #getObjectType()} {@link #getObject()} invocations may arrive early in the + * bootstrap process, even ahead of any post-processor setup. If you need access to * other beans, implement {@link BeanFactoryAware} and obtain them programmatically. * + *

The container is only responsible for managing the lifecycle of the FactoryBean + * instance, not the lifecycle of the objects created by the FactoryBean. Therefore, + * a destroy method on an exposed bean object (such as {@link java.io.Closeable#close()} + * will not be called automatically. Instead, a FactoryBean should implement + * {@link DisposableBean} and delegate any such close call to the underlying object. + * *

Finally, FactoryBean objects participate in the containing BeanFactory's * synchronization of bean creation. There is usually no need for internal * synchronization other than for purposes of lazy initialization within the diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java index 09f0f5dade56..389f19c9e488 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java @@ -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. @@ -39,7 +39,7 @@ * They will ignore any singleton beans that have been registered by other means like * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}'s * {@code registerSingleton} method, with the exception of - * {@code getBeanNamesOfType} and {@code getBeansOfType} which will check + * {@code getBeanNamesForType} and {@code getBeansOfType} which will check * such manually registered singletons too. Of course, BeanFactory's {@code getBean} * does allow transparent access to such special beans as well. However, in typical * scenarios, all beans will be defined by external bean definitions anyway, so most @@ -87,6 +87,43 @@ public interface ListableBeanFactory extends BeanFactory { */ String[] getBeanDefinitionNames(); + /** + * Return a provider for the specified bean, allowing for lazy on-demand retrieval + * of instances, including availability and uniqueness options. + * @param requiredType type the bean must match; can be an interface or superclass + * @param allowEagerInit whether stream-based access may initialize lazy-init + * singletons and objects created by FactoryBeans (or by factory methods + * with a "factory-bean" reference) for the type check + * @return a corresponding provider handle + * @since 5.3 + * @see #getBeanProvider(ResolvableType, boolean) + * @see #getBeanProvider(Class) + * @see #getBeansOfType(Class, boolean, boolean) + * @see #getBeanNamesForType(Class, boolean, boolean) + */ + ObjectProvider getBeanProvider(Class requiredType, boolean allowEagerInit); + + /** + * Return a provider for the specified bean, allowing for lazy on-demand retrieval + * of instances, including availability and uniqueness options. + * @param requiredType type the bean must match; can be a generic type declaration. + * Note that collection types are not supported here, in contrast to reflective + * injection points. For programmatically retrieving a list of beans matching a + * specific type, specify the actual bean type as an argument here and subsequently + * use {@link ObjectProvider#orderedStream()} or its lazy streaming/iteration options. + * @param allowEagerInit whether stream-based access may initialize lazy-init + * singletons and objects created by FactoryBeans (or by factory methods + * with a "factory-bean" reference) for the type check + * @return a corresponding provider handle + * @since 5.3 + * @see #getBeanProvider(ResolvableType) + * @see ObjectProvider#iterator() + * @see ObjectProvider#stream() + * @see ObjectProvider#orderedStream() + * @see #getBeanNamesForType(ResolvableType, boolean, boolean) + */ + ObjectProvider getBeanProvider(ResolvableType requiredType, boolean allowEagerInit); + /** * Return the names of beans matching the given type (including subclasses), * judging from either bean definitions or the value of {@code getObjectType} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotationBeanWiringInfoResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotationBeanWiringInfoResolver.java index a0e05da517b2..b3550404c5c5 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotationBeanWiringInfoResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotationBeanWiringInfoResolver.java @@ -56,7 +56,7 @@ protected BeanWiringInfo buildWiringInfo(Object beanInstance, Configurable annot // Autowiring by name or by type return new BeanWiringInfo(annotation.autowire().value(), annotation.dependencyCheck()); } - else if (!"".equals(annotation.value())) { + else if (!annotation.value().isEmpty()) { // Explicitly specified bean name for bean definition to take property values from return new BeanWiringInfo(annotation.value(), false); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Autowired.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Autowired.java index 36e91744666b..242fddbad8e1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Autowired.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Autowired.java @@ -36,9 +36,11 @@ * annotation, they will be considered as candidates for autowiring. The constructor * with the greatest number of dependencies that can be satisfied by matching beans * in the Spring container will be chosen. If none of the candidates can be satisfied, - * then a primary/default constructor (if present) will be used. If a class only - * declares a single constructor to begin with, it will always be used, even if not - * annotated. An annotated constructor does not have to be public. + * then a primary/default constructor (if present) will be used. Similarly, if a + * class declares multiple constructors but none of them is annotated with + * {@code @Autowired}, then a primary/default constructor (if present) will be used. + * If a class only declares a single constructor to begin with, it will always be used, + * even if not annotated. An annotated constructor does not have to be public. * *

Autowired Fields

*

Fields are injected right after construction of a bean, before any config methods diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index 4b2d208a32e2..2065f6b6e9b0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,7 +50,7 @@ import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; -import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; +import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.support.LookupOverride; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -128,8 +128,8 @@ * @see Autowired * @see Value */ -public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter - implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware { +public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor, + MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware { protected final Log logger = LogFactory.getLog(getClass()); @@ -153,7 +153,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean /** * Create a new {@code AutowiredAnnotationBeanPostProcessor} for Spring's - * standard {@link Autowired @Autowired} annotation. + * standard {@link Autowired @Autowired} and {@link Value @Value} annotations. *

Also supports JSR-330's {@link javax.inject.Inject @Inject} annotation, * if available. */ @@ -174,9 +174,10 @@ public AutowiredAnnotationBeanPostProcessor() { /** * Set the 'autowired' annotation type, to be used on constructors, fields, - * setter methods and arbitrary config methods. - *

The default autowired annotation type is the Spring-provided {@link Autowired} - * annotation, as well as {@link Value}. + * setter methods, and arbitrary config methods. + *

The default autowired annotation types are the Spring-provided + * {@link Autowired @Autowired} and {@link Value @Value} annotations as well + * as JSR-330's {@link javax.inject.Inject @Inject} annotation, if available. *

This setter property exists so that developers can provide their own * (non-Spring-specific) annotation type to indicate that a member is supposed * to be autowired. @@ -189,9 +190,10 @@ public void setAutowiredAnnotationType(Class autowiredAnno /** * Set the 'autowired' annotation types, to be used on constructors, fields, - * setter methods and arbitrary config methods. - *

The default autowired annotation type is the Spring-provided {@link Autowired} - * annotation, as well as {@link Value}. + * setter methods, and arbitrary config methods. + *

The default autowired annotation types are the Spring-provided + * {@link Autowired @Autowired} and {@link Value @Value} annotations as well + * as JSR-330's {@link javax.inject.Inject @Inject} annotation, if available. *

This setter property exists so that developers can provide their own * (non-Spring-specific) annotation types to indicate that a member is supposed * to be autowired. @@ -203,7 +205,7 @@ public void setAutowiredAnnotationTypes(Set> autowir } /** - * Set the name of a parameter of the annotation that specifies whether it is required. + * Set the name of an attribute of the annotation that specifies whether it is required. * @see #setRequiredParameterValue(boolean) */ public void setRequiredParameterName(String requiredParameterName) { @@ -211,7 +213,7 @@ public void setRequiredParameterName(String requiredParameterName) { } /** - * Set the boolean value that marks a dependency as required + * Set the boolean value that marks a dependency as required. *

For example if using 'required=true' (the default), this value should be * {@code true}; but if using 'optional=false', this value should be {@code false}. * @see #setRequiredParameterName(String) @@ -415,9 +417,11 @@ public PropertyValues postProcessPropertyValues( /** * 'Native' processing method for direct calls with an arbitrary target instance, - * resolving all of its fields and methods which are annotated with {@code @Autowired}. + * resolving all of its fields and methods which are annotated with one of the + * configured 'autowired' annotation types. * @param bean the target instance to process * @throws BeanCreationException if autowiring failed + * @see #setAutowiredAnnotationTypes(Set) */ public void processInjection(Object bean) throws BeanCreationException { Class clazz = bean.getClass(); @@ -609,7 +613,7 @@ private class AutowiredFieldElement extends InjectionMetadata.InjectedElement { private final boolean required; - private volatile boolean cached = false; + private volatile boolean cached; @Nullable private volatile Object cachedFieldValue; @@ -624,46 +628,58 @@ protected void inject(Object bean, @Nullable String beanName, @Nullable Property Field field = (Field) this.member; Object value; if (this.cached) { - value = resolvedCachedArgument(beanName, this.cachedFieldValue); - } - else { - DependencyDescriptor desc = new DependencyDescriptor(field, this.required); - desc.setContainingClass(bean.getClass()); - Set autowiredBeanNames = new LinkedHashSet<>(1); - Assert.state(beanFactory != null, "No BeanFactory available"); - TypeConverter typeConverter = beanFactory.getTypeConverter(); try { - value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); + value = resolvedCachedArgument(beanName, this.cachedFieldValue); } - catch (BeansException ex) { - throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); - } - synchronized (this) { - if (!this.cached) { - if (value != null || this.required) { - this.cachedFieldValue = desc; - registerDependentBeans(beanName, autowiredBeanNames); - if (autowiredBeanNames.size() == 1) { - String autowiredBeanName = autowiredBeanNames.iterator().next(); - if (beanFactory.containsBean(autowiredBeanName) && - beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { - this.cachedFieldValue = new ShortcutDependencyDescriptor( - desc, autowiredBeanName, field.getType()); - } - } - } - else { - this.cachedFieldValue = null; - } - this.cached = true; - } + catch (NoSuchBeanDefinitionException ex) { + // Unexpected removal of target bean for cached argument -> re-resolve + value = resolveFieldValue(field, bean, beanName); } } + else { + value = resolveFieldValue(field, bean, beanName); + } if (value != null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); } } + + @Nullable + private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) { + DependencyDescriptor desc = new DependencyDescriptor(field, this.required); + desc.setContainingClass(bean.getClass()); + Set autowiredBeanNames = new LinkedHashSet<>(1); + Assert.state(beanFactory != null, "No BeanFactory available"); + TypeConverter typeConverter = beanFactory.getTypeConverter(); + Object value; + try { + value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); + } + catch (BeansException ex) { + throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); + } + synchronized (this) { + if (!this.cached) { + Object cachedFieldValue = null; + if (value != null || this.required) { + cachedFieldValue = desc; + registerDependentBeans(beanName, autowiredBeanNames); + if (autowiredBeanNames.size() == 1) { + String autowiredBeanName = autowiredBeanNames.iterator().next(); + if (beanFactory.containsBean(autowiredBeanName) && + beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { + cachedFieldValue = new ShortcutDependencyDescriptor( + desc, autowiredBeanName, field.getType()); + } + } + } + this.cachedFieldValue = cachedFieldValue; + this.cached = true; + } + } + return value; + } } @@ -674,7 +690,7 @@ private class AutowiredMethodElement extends InjectionMetadata.InjectedElement { private final boolean required; - private volatile boolean cached = false; + private volatile boolean cached; @Nullable private volatile Object[] cachedMethodArguments; @@ -692,58 +708,17 @@ protected void inject(Object bean, @Nullable String beanName, @Nullable Property Method method = (Method) this.member; Object[] arguments; if (this.cached) { - // Shortcut for avoiding synchronization... - arguments = resolveCachedArguments(beanName); - } - else { - Class[] paramTypes = method.getParameterTypes(); - arguments = new Object[paramTypes.length]; - DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length]; - Set autowiredBeans = new LinkedHashSet<>(paramTypes.length); - Assert.state(beanFactory != null, "No BeanFactory available"); - TypeConverter typeConverter = beanFactory.getTypeConverter(); - for (int i = 0; i < arguments.length; i++) { - MethodParameter methodParam = new MethodParameter(method, i); - DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required); - currDesc.setContainingClass(bean.getClass()); - descriptors[i] = currDesc; - try { - Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter); - if (arg == null && !this.required) { - arguments = null; - break; - } - arguments[i] = arg; - } - catch (BeansException ex) { - throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex); - } + try { + arguments = resolveCachedArguments(beanName); } - synchronized (this) { - if (!this.cached) { - if (arguments != null) { - DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length); - registerDependentBeans(beanName, autowiredBeans); - if (autowiredBeans.size() == paramTypes.length) { - Iterator it = autowiredBeans.iterator(); - for (int i = 0; i < paramTypes.length; i++) { - String autowiredBeanName = it.next(); - if (beanFactory.containsBean(autowiredBeanName) && - beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) { - cachedMethodArguments[i] = new ShortcutDependencyDescriptor( - descriptors[i], autowiredBeanName, paramTypes[i]); - } - } - } - this.cachedMethodArguments = cachedMethodArguments; - } - else { - this.cachedMethodArguments = null; - } - this.cached = true; - } + catch (NoSuchBeanDefinitionException ex) { + // Unexpected removal of target bean for cached argument -> re-resolve + arguments = resolveMethodArguments(method, bean, beanName); } } + else { + arguments = resolveMethodArguments(method, bean, beanName); + } if (arguments != null) { try { ReflectionUtils.makeAccessible(method); @@ -767,6 +742,59 @@ private Object[] resolveCachedArguments(@Nullable String beanName) { } return arguments; } + + @Nullable + private Object[] resolveMethodArguments(Method method, Object bean, @Nullable String beanName) { + int argumentCount = method.getParameterCount(); + Object[] arguments = new Object[argumentCount]; + DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount]; + Set autowiredBeans = new LinkedHashSet<>(argumentCount); + Assert.state(beanFactory != null, "No BeanFactory available"); + TypeConverter typeConverter = beanFactory.getTypeConverter(); + for (int i = 0; i < arguments.length; i++) { + MethodParameter methodParam = new MethodParameter(method, i); + DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required); + currDesc.setContainingClass(bean.getClass()); + descriptors[i] = currDesc; + try { + Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter); + if (arg == null && !this.required) { + arguments = null; + break; + } + arguments[i] = arg; + } + catch (BeansException ex) { + throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex); + } + } + synchronized (this) { + if (!this.cached) { + if (arguments != null) { + DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length); + registerDependentBeans(beanName, autowiredBeans); + if (autowiredBeans.size() == argumentCount) { + Iterator it = autowiredBeans.iterator(); + Class[] paramTypes = method.getParameterTypes(); + for (int i = 0; i < paramTypes.length; i++) { + String autowiredBeanName = it.next(); + if (beanFactory.containsBean(autowiredBeanName) && + beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) { + cachedMethodArguments[i] = new ShortcutDependencyDescriptor( + descriptors[i], autowiredBeanName, paramTypes[i]); + } + } + } + this.cachedMethodArguments = cachedMethodArguments; + } + else { + this.cachedMethodArguments = null; + } + this.cached = true; + } + } + return arguments; + } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java index 1d1fbd77cb72..f7dcb8d18cf1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java @@ -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. @@ -26,9 +26,6 @@ import java.util.LinkedHashSet; import java.util.Set; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -53,6 +50,10 @@ public class InjectionMetadata { * @since 5.2 */ public static final InjectionMetadata EMPTY = new InjectionMetadata(Object.class, Collections.emptyList()) { + @Override + protected boolean needsRefresh(Class clazz) { + return false; + } @Override public void checkConfigMembers(RootBeanDefinition beanDefinition) { } @@ -65,8 +66,6 @@ public void clear(@Nullable PropertyValues pvs) { }; - private static final Log logger = LogFactory.getLog(InjectionMetadata.class); - private final Class targetClass; private final Collection injectedElements; @@ -89,6 +88,16 @@ public InjectionMetadata(Class targetClass, Collection eleme } + /** + * Determine whether this metadata instance needs to be refreshed. + * @param clazz the current target class + * @return {@code true} indicating a refresh, {@code false} otherwise + * @since 5.2.4 + */ + protected boolean needsRefresh(Class clazz) { + return this.targetClass != clazz; + } + public void checkConfigMembers(RootBeanDefinition beanDefinition) { Set checkedElements = new LinkedHashSet<>(this.injectedElements.size()); for (InjectedElement element : this.injectedElements) { @@ -96,9 +105,6 @@ public void checkConfigMembers(RootBeanDefinition beanDefinition) { if (!beanDefinition.isExternallyManagedConfigMember(member)) { beanDefinition.registerExternallyManagedConfigMember(member); checkedElements.add(element); - if (logger.isTraceEnabled()) { - logger.trace("Registered injected element on class [" + this.targetClass.getName() + "]: " + element); - } } } this.checkedElements = checkedElements; @@ -110,9 +116,6 @@ public void inject(Object target, @Nullable String beanName, @Nullable PropertyV (checkedElements != null ? checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) { for (InjectedElement element : elementsToIterate) { - if (logger.isTraceEnabled()) { - logger.trace("Processing injected element of bean '" + beanName + "': " + element); - } element.inject(target, beanName, pvs); } } @@ -138,12 +141,12 @@ public void clear(@Nullable PropertyValues pvs) { * 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 {@code InjectionMetadata} 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 elements, Class clazz) { - return (elements.isEmpty() ? InjectionMetadata.EMPTY : new InjectionMetadata(clazz, elements)); + return (elements.isEmpty() ? new InjectionMetadata(clazz, Collections.emptyList()) : + new InjectionMetadata(clazz, elements)); } /** @@ -151,9 +154,10 @@ public static InjectionMetadata forElements(Collection elements * @param metadata the existing metadata instance * @param clazz the current target class * @return {@code true} indicating a refresh, {@code false} otherwise + * @see #needsRefresh(Class) */ public static boolean needsRefresh(@Nullable InjectionMetadata metadata, Class clazz) { - return (metadata == null || metadata.targetClass != clazz); + return (metadata == null || metadata.needsRefresh(clazz)); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Lookup.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Lookup.java index 495354aa20c6..0fca4f3316a0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Lookup.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Lookup.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 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. @@ -41,12 +41,11 @@ * regular constructors: i.e. lookup methods cannot get replaced on beans returned * from factory methods where we cannot dynamically provide a subclass for them. * - *

Concrete limitations in typical Spring configuration scenarios: - * When used with component scanning or any other mechanism that filters out abstract - * beans, provide stub implementations of your lookup methods to be able to declare - * them as concrete classes. And please remember that lookup methods won't work on - * beans returned from {@code @Bean} methods in configuration classes; you'll have - * to resort to {@code @Inject Provider} or the like instead. + *

Recommendations for typical Spring configuration scenarios: + * When a concrete class may be needed in certain scenarios, consider providing stub + * implementations of your lookup methods. And please remember that lookup methods + * won't work on beans returned from {@code @Bean} methods in configuration classes; + * you'll have to resort to {@code @Inject Provider} or the like instead. * * @author Juergen Hoeller * @since 4.1 diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java index 3a3b0944c236..e4e104b4c8e4 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,7 +40,6 @@ import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; /** * {@link AutowireCandidateResolver} implementation that matches bean definition qualifiers @@ -188,7 +187,7 @@ protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] an foundMeta = true; // Only accept fallback match if @Qualifier annotation has a value... // Otherwise it is just a marker for a custom qualifier annotation. - if ((fallbackToMeta && StringUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) || + if ((fallbackToMeta && ObjectUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) || !checkQualifier(bdHolder, metaAnn, typeConverter)) { return false; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java index 67d9b52cd2ef..9978f979b506 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java @@ -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. @@ -31,7 +31,7 @@ import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; +import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.Conventions; @@ -75,8 +75,8 @@ * (or a custom {@link org.springframework.beans.factory.InitializingBean} implementation) */ @Deprecated -public class RequiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter - implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware { +public class RequiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor, + MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware { /** * Bean definition attribute that may indicate whether a given bean is supposed diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Value.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Value.java index 36b1139e4cbc..bfa27305a6a1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Value.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Value.java @@ -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. @@ -23,14 +23,17 @@ import java.lang.annotation.Target; /** - * Annotation at the field or method/constructor parameter level - * that indicates a default value expression for the affected argument. + * Annotation used at the field or method/constructor parameter level + * that indicates a default value expression for the annotated element. * - *

Typically used for expression-driven dependency injection. Also supported - * for dynamic resolution of handler method parameters, e.g. in Spring MVC. + *

Typically used for expression-driven or property-driven dependency injection. + * Also supported for dynamic resolution of handler method arguments — for + * example, in Spring MVC. * - *

A common use case is to assign default field values using - * #{systemProperties.myProp} style expressions. + *

A common use case is to inject values using + * #{systemProperties.myProp} style SpEL (Spring Expression Language) + * expressions. Alternatively, values may be injected using + * ${my.app.myProp} style property placeholders. * *

Note that actual processing of the {@code @Value} annotation is performed * by a {@link org.springframework.beans.factory.config.BeanPostProcessor @@ -55,7 +58,8 @@ public @interface Value { /** - * The actual value expression — for example, #{systemProperties.myProp}. + * The actual value expression such as #{systemProperties.myProp} + * or property placeholder such as ${my.app.myProp}. */ String value(); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java index f22b60821288..5be39a0eaa11 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java @@ -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. @@ -41,16 +41,18 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { /** - * Scope identifier for the standard singleton scope: "singleton". + * Scope identifier for the standard singleton scope: {@value}. *

Note that extended bean factories might support further scopes. * @see #setScope + * @see ConfigurableBeanFactory#SCOPE_SINGLETON */ String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; /** - * Scope identifier for the standard prototype scope: "prototype". + * Scope identifier for the standard prototype scope: {@value}. *

Note that extended bean factories might support further scopes. * @see #setScope + * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE */ String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; @@ -236,7 +238,7 @@ default boolean hasConstructorArgumentValues() { MutablePropertyValues getPropertyValues(); /** - * Return if there are property values values defined for this bean. + * Return if there are property values defined for this bean. * @since 5.0.2 */ default boolean hasPropertyValues() { @@ -271,7 +273,7 @@ default boolean hasPropertyValues() { /** * Set the role hint for this {@code BeanDefinition}. The role hint - * provides the frameworks as well as tools with an indication of + * provides the frameworks as well as tools an indication of * the role and importance of a particular {@code BeanDefinition}. * @since 5.1 * @see #ROLE_APPLICATION @@ -282,7 +284,7 @@ default boolean hasPropertyValues() { /** * Get the role hint for this {@code BeanDefinition}. The role hint - * provides the frameworks as well as tools with an indication of + * provides the frameworks as well as tools an indication of * the role and importance of a particular {@code BeanDefinition}. * @see #ROLE_APPLICATION * @see #ROLE_SUPPORT @@ -345,7 +347,7 @@ default boolean hasPropertyValues() { /** * Return the originating BeanDefinition, or {@code null} if none. - * Allows for retrieving the decorated bean definition, if any. + *

Allows for retrieving the decorated bean definition, if any. *

Note that this method returns the immediate originator. Iterate through the * originator chain to find the original BeanDefinition as defined by the user. */ diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java index 98aa3a87ccfd..81f4d8760154 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java @@ -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. @@ -27,6 +27,7 @@ import org.springframework.beans.factory.HierarchicalBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.core.convert.ConversionService; +import org.springframework.core.metrics.ApplicationStartup; import org.springframework.lang.Nullable; import org.springframework.util.StringValueResolver; @@ -51,15 +52,15 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry { /** - * Scope identifier for the standard singleton scope: "singleton". - * Custom scopes can be added via {@code registerScope}. + * Scope identifier for the standard singleton scope: {@value}. + *

Custom scopes can be added via {@code registerScope}. * @see #registerScope */ String SCOPE_SINGLETON = "singleton"; /** - * Scope identifier for the standard prototype scope: "prototype". - * Custom scopes can be added via {@code registerScope}. + * Scope identifier for the standard prototype scope: {@value}. + *

Custom scopes can be added via {@code registerScope}. * @see #registerScope */ String SCOPE_PROTOTYPE = "prototype"; @@ -276,6 +277,20 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single @Nullable Scope getRegisteredScope(String scopeName); + /** + * Set the {@code ApplicationStartup} for this bean factory. + *

This allows the application context to record metrics during application startup. + * @param applicationStartup the new application startup + * @since 5.3 + */ + void setApplicationStartup(ApplicationStartup applicationStartup); + + /** + * Return the {@code ApplicationStartup} for this bean factory. + * @since 5.3 + */ + ApplicationStartup getApplicationStartup(); + /** * Provides a security access control context relevant to this factory. * @return the applicable AccessControlContext (never {@code null}) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java index 45da25fa84d0..4fbfb435875d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java @@ -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. @@ -164,10 +164,10 @@ public ValueHolder getIndexedArgumentValue(int index, @Nullable Class require Assert.isTrue(index >= 0, "Index must not be negative"); ValueHolder valueHolder = this.indexedArgumentValues.get(index); if (valueHolder != null && - (valueHolder.getType() == null || - (requiredType != null && ClassUtils.matchesTypeName(requiredType, valueHolder.getType()))) && - (valueHolder.getName() == null || "".equals(requiredName) || - (requiredName != null && requiredName.equals(valueHolder.getName())))) { + (valueHolder.getType() == null || (requiredType != null && + ClassUtils.matchesTypeName(requiredType, valueHolder.getType()))) && + (valueHolder.getName() == null || (requiredName != null && + (requiredName.isEmpty() || requiredName.equals(valueHolder.getName()))))) { return valueHolder; } return null; @@ -277,17 +277,19 @@ public ValueHolder getGenericArgumentValue(Class requiredType, String require * @return the ValueHolder for the argument, or {@code null} if none found */ @Nullable - public ValueHolder getGenericArgumentValue(@Nullable Class requiredType, @Nullable String requiredName, @Nullable Set usedValueHolders) { + public ValueHolder getGenericArgumentValue(@Nullable Class requiredType, @Nullable String requiredName, + @Nullable Set usedValueHolders) { + for (ValueHolder valueHolder : this.genericArgumentValues) { if (usedValueHolders != null && usedValueHolders.contains(valueHolder)) { continue; } - if (valueHolder.getName() != null && !"".equals(requiredName) && - (requiredName == null || !valueHolder.getName().equals(requiredName))) { + if (valueHolder.getName() != null && (requiredName == null || + (!requiredName.isEmpty() && !requiredName.equals(valueHolder.getName())))) { continue; } - if (valueHolder.getType() != null && - (requiredType == null || !ClassUtils.matchesTypeName(requiredType, valueHolder.getType()))) { + if (valueHolder.getType() != null && (requiredType == null || + !ClassUtils.matchesTypeName(requiredType, valueHolder.getType()))) { continue; } if (requiredType != null && valueHolder.getType() == null && valueHolder.getName() == null && diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java index d63a3ddfc044..d5fd082162b6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java @@ -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. @@ -371,7 +371,7 @@ public void initParameterNameDiscovery(@Nullable ParameterNameDiscoverer paramet /** * Determine the name of the wrapped parameter/field. - * @return the declared name (never {@code null}) + * @return the declared name (may be {@code null} if unresolvable) */ @Nullable public String getDependencyName() { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java index 887afb947130..b3c112c8be46 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java @@ -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. @@ -16,13 +16,6 @@ package org.springframework.beans.factory.config; -import java.beans.PropertyDescriptor; -import java.lang.reflect.Constructor; - -import org.springframework.beans.BeansException; -import org.springframework.beans.PropertyValues; -import org.springframework.lang.Nullable; - /** * Adapter that implements all methods on {@link SmartInstantiationAwareBeanPostProcessor} * as no-ops, which will not change normal processing of each bean instantiated @@ -37,60 +30,10 @@ * @author Rod Johnson * @author Juergen Hoeller * @since 2.0 + * @deprecated as of 5.3 in favor of implementing {@link InstantiationAwareBeanPostProcessor} + * or {@link SmartInstantiationAwareBeanPostProcessor} directly. */ +@Deprecated public abstract class InstantiationAwareBeanPostProcessorAdapter implements SmartInstantiationAwareBeanPostProcessor { - @Override - @Nullable - public Class predictBeanType(Class beanClass, String beanName) throws BeansException { - return null; - } - - @Override - @Nullable - public Constructor[] determineCandidateConstructors(Class beanClass, String beanName) throws BeansException { - return null; - } - - @Override - public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { - return bean; - } - - @Override - @Nullable - public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { - return null; - } - - @Override - public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { - return true; - } - - @Override - public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) - throws BeansException { - - return null; - } - - @Deprecated - @Override - public PropertyValues postProcessPropertyValues( - PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { - - return pvs; - } - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - return bean; - } - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - return bean; - } - } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/MapFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/MapFactoryBean.java index 6c11998e416f..b02673c8b789 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/MapFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/MapFactoryBean.java @@ -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. @@ -16,13 +16,13 @@ package org.springframework.beans.factory.config; -import java.util.LinkedHashMap; import java.util.Map; import org.springframework.beans.BeanUtils; import org.springframework.beans.TypeConverter; import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; +import org.springframework.util.CollectionUtils; /** * Simple factory for shared Map instances. Allows for central setup @@ -85,7 +85,7 @@ protected Map createInstance() { result = BeanUtils.instantiateClass(this.targetMapClass); } else { - result = new LinkedHashMap<>(this.sourceMap.size()); + result = CollectionUtils.newLinkedHashMap(this.sourceMap.size()); } Class keyType = null; Class valueType = null; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/PlaceholderConfigurerSupport.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/PlaceholderConfigurerSupport.java index fc29a9eda995..9a11f7af3ff2 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/PlaceholderConfigurerSupport.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/PlaceholderConfigurerSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,15 +36,16 @@ * Example XML bean definition: * *

- * <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"/>
- *   <property name="driverClassName" value="${driver}"/>
- *   <property name="url" value="jdbc:${dbname}"/>
+ * <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
+ *   <property name="driverClassName" value="${driver}" />
+ *   <property name="url" value="jdbc:${dbname}" />
  * </bean>
  * 
* * Example properties file: * - *
driver=com.mysql.jdbc.Driver
+ * 
+ * driver=com.mysql.jdbc.Driver
  * dbname=mysql:mydb
* * Annotated bean definitions may take advantage of property replacement using @@ -56,7 +57,8 @@ * in bean references. Furthermore, placeholder values can also cross-reference * other placeholders, like: * - *
rootPath=myrootdir
+ * 
+ * rootPath=myrootdir
  * subPath=${rootPath}/subdir
* * In contrast to {@link PropertyOverrideConfigurer}, subclasses of this type allow @@ -71,13 +73,13 @@ * *

Default property values can be defined globally for each configurer instance * via the {@link #setProperties properties} property, or on a property-by-property basis - * using the default value separator which is {@code ":"} by default and - * customizable via {@link #setValueSeparator(String)}. + * using the value separator which is {@code ":"} by default and customizable via + * {@link #setValueSeparator(String)}. * *

Example XML property with default value: * *

- *   
+ *   <property name="url" value="jdbc:${dbname:defaultdb}" />
  * 
* * @author Chris Beams diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java index 340dee2dcac6..e34d7c25ce83 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,17 +25,23 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.reader.UnicodeReader; +import org.yaml.snakeyaml.representer.Representer; import org.springframework.core.CollectionFactory; import org.springframework.core.io.Resource; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -45,6 +51,8 @@ * * @author Dave Syer * @author Juergen Hoeller + * @author Sam Brannen + * @author Brian Clozel * @since 4.1 */ public abstract class YamlProcessor { @@ -59,6 +67,8 @@ public abstract class YamlProcessor { private boolean matchDefault = true; + private Set supportedTypes = Collections.emptySet(); + /** * A map of document matchers allowing callers to selectively use only @@ -117,6 +127,28 @@ public void setResources(Resource... resources) { this.resources = resources; } + /** + * Set the supported types that can be loaded from YAML documents. + *

If no supported types are configured, only Java standard classes + * (as defined in {@link org.yaml.snakeyaml.constructor.SafeConstructor}) + * encountered in YAML documents will be supported. + * If an unsupported type is encountered, an {@link IllegalStateException} + * will be thrown when the corresponding YAML node is processed. + * @param supportedTypes the supported types, or an empty array to clear the + * supported types + * @since 5.1.16 + * @see #createYaml() + */ + public void setSupportedTypes(Class... supportedTypes) { + if (ObjectUtils.isEmpty(supportedTypes)) { + this.supportedTypes = Collections.emptySet(); + } + else { + Assert.noNullElements(supportedTypes, "'supportedTypes' must not contain null elements"); + this.supportedTypes = Arrays.stream(supportedTypes).map(Class::getName) + .collect(Collectors.collectingAndThen(Collectors.toSet(), Collections::unmodifiableSet)); + } + } /** * Provide an opportunity for subclasses to process the Yaml parsed from the supplied @@ -142,12 +174,18 @@ protected void process(MatchCallback callback) { * Create the {@link Yaml} instance to use. *

The default implementation sets the "allowDuplicateKeys" flag to {@code false}, * enabling built-in duplicate key handling in SnakeYAML 1.18+. + *

As of Spring Framework 5.1.16, if custom {@linkplain #setSupportedTypes + * supported types} have been configured, the default implementation creates + * a {@code Yaml} instance that filters out unsupported types encountered in + * YAML documents. If an unsupported type is encountered, an + * {@link IllegalStateException} will be thrown when the node is processed. * @see LoaderOptions#setAllowDuplicateKeys(boolean) */ protected Yaml createYaml() { - LoaderOptions options = new LoaderOptions(); - options.setAllowDuplicateKeys(false); - return new Yaml(options); + LoaderOptions loaderOptions = new LoaderOptions(); + loaderOptions.setAllowDuplicateKeys(false); + return new Yaml(new FilteringConstructor(loaderOptions), new Representer(), + new DumperOptions(), loaderOptions); } private boolean process(MatchCallback callback, Yaml yaml, Resource resource) { @@ -311,6 +349,7 @@ else if (value instanceof Collection) { /** * Callback interface used to process the YAML parsing results. */ + @FunctionalInterface public interface MatchCallback { /** @@ -327,6 +366,7 @@ public interface MatchCallback { /** * Strategy interface used to test if properties match. */ + @FunctionalInterface public interface DocumentMatcher { /** @@ -388,4 +428,24 @@ public enum ResolutionMethod { FIRST_FOUND } + + /** + * {@link Constructor} that supports filtering of unsupported types. + *

If an unsupported type is encountered in a YAML document, an + * {@link IllegalStateException} will be thrown from {@link #getClassForName}. + */ + private class FilteringConstructor extends Constructor { + + FilteringConstructor(LoaderOptions loaderOptions) { + super(loaderOptions); + } + + @Override + protected Class getClassForName(String name) throws ClassNotFoundException { + Assert.state(YamlProcessor.this.supportedTypes.contains(name), + () -> "Unsupported type encountered in YAML document: " + name); + return super.getClassForName(name); + } + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java index c82964c23276..14427fee93ec 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -232,7 +232,6 @@ public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreExce * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ - @SuppressWarnings("rawtypes") public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { // Check for XML files and redirect them to the "standard" XmlBeanDefinitionReader String filename = encodedResource.getResource().getFilename(); @@ -245,10 +244,10 @@ public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefin } @SuppressWarnings("serial") - Closure beans = new Closure(this) { + Closure beans = new Closure(this) { @Override public Object call(Object... args) { - invokeBeanDefiningClosure((Closure) args[0]); + invokeBeanDefiningClosure((Closure) args[0]); return null; } }; @@ -290,8 +289,7 @@ public void setVariable(String name, Object value) { * @param closure the block or closure * @return this {@code GroovyBeanDefinitionReader} instance */ - @SuppressWarnings("rawtypes") - public GroovyBeanDefinitionReader beans(Closure closure) { + public GroovyBeanDefinitionReader beans(Closure closure) { return invokeBeanDefiningClosure(closure); } @@ -312,29 +310,25 @@ public GenericBeanDefinition bean(Class type) { * @param args the constructors arguments and closure configurer * @return the bean definition */ - @SuppressWarnings("rawtypes") public AbstractBeanDefinition bean(Class type, Object...args) { GroovyBeanDefinitionWrapper current = this.currentBeanDefinition; try { - Closure callable = null; - Collection constructorArgs = null; + Closure callable = null; + Collection constructorArgs = null; if (!ObjectUtils.isEmpty(args)) { int index = args.length; Object lastArg = args[index - 1]; - if (lastArg instanceof Closure) { - callable = (Closure) lastArg; + if (lastArg instanceof Closure) { + callable = (Closure) lastArg; index--; } - if (index > -1) { - constructorArgs = resolveConstructorArguments(args, 0, index); - } + constructorArgs = resolveConstructorArguments(args, 0, index); } this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(null, type, constructorArgs); if (callable != null) { callable.call(this.currentBeanDefinition); } return this.currentBeanDefinition.getBeanDefinition(); - } finally { this.currentBeanDefinition = current; @@ -381,11 +375,10 @@ public void importBeans(String resourcePattern) throws IOException { * takes a class argument. */ @Override - @SuppressWarnings("rawtypes") public Object invokeMethod(String name, Object arg) { Object[] args = (Object[])arg; if ("beans".equals(name) && args.length == 1 && args[0] instanceof Closure) { - return beans((Closure) args[0]); + return beans((Closure) args[0]); } else if ("ref".equals(name)) { String refName; @@ -435,14 +428,13 @@ private boolean addDeferredProperty(String property, Object newValue) { return false; } - @SuppressWarnings("rawtypes") private void finalizeDeferredProperties() { for (DeferredProperty dp : this.deferredProperties.values()) { if (dp.value instanceof List) { - dp.value = manageListIfNecessary((List) dp.value); + dp.value = manageListIfNecessary((List) dp.value); } else if (dp.value instanceof Map) { - dp.value = manageMapIfNecessary((Map) dp.value); + dp.value = manageMapIfNecessary((Map) dp.value); } dp.apply(); } @@ -454,8 +446,7 @@ else if (dp.value instanceof Map) { * @param callable the closure argument * @return this {@code GroovyBeanDefinitionReader} instance */ - @SuppressWarnings("rawtypes") - protected GroovyBeanDefinitionReader invokeBeanDefiningClosure(Closure callable) { + protected GroovyBeanDefinitionReader invokeBeanDefiningClosure(Closure callable) { callable.setDelegate(this); callable.call(); finalizeDeferredProperties(); @@ -469,7 +460,6 @@ protected GroovyBeanDefinitionReader invokeBeanDefiningClosure(Closure callable) * argument is sometimes a closure. All the arguments in between are constructor arguments. * @return the bean definition wrapper */ - @SuppressWarnings("rawtypes") private GroovyBeanDefinitionWrapper invokeBeanDefiningMethod(String beanName, Object[] args) { boolean hasClosureArgument = (args[args.length - 1] instanceof Closure); if (args[0] instanceof Class) { @@ -495,11 +485,12 @@ else if (args[0] instanceof RuntimeBeanReference) { else if (args[0] instanceof Map) { // named constructor arguments if (args.length > 1 && args[1] instanceof Class) { - List constructorArgs = resolveConstructorArguments(args, 2, hasClosureArgument ? args.length - 1 : args.length); + List constructorArgs = + resolveConstructorArguments(args, 2, hasClosureArgument ? args.length - 1 : args.length); this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, (Class) args[1], constructorArgs); - Map namedArgs = (Map) args[0]; - for (Object o : namedArgs.keySet()) { - String propName = (String) o; + Map namedArgs = (Map) args[0]; + for (Object key : namedArgs.keySet()) { + String propName = (String) key; setProperty(propName, namedArgs.get(propName)); } } @@ -507,7 +498,7 @@ else if (args[0] instanceof Map) { else { this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName); // First arg is the map containing factoryBean : factoryMethod - Map.Entry factoryBeanEntry = (Map.Entry) ((Map) args[0]).entrySet().iterator().next(); + Map.Entry factoryBeanEntry = ((Map) args[0]).entrySet().iterator().next(); // If we have a closure body, that will be the last argument. // In between are the constructor args int constructorArgsTest = (hasClosureArgument ? 2 : 1); @@ -531,12 +522,13 @@ else if (args[0] instanceof Closure) { this.currentBeanDefinition.getBeanDefinition().setAbstract(true); } else { - List constructorArgs = resolveConstructorArguments(args, 0, hasClosureArgument ? args.length - 1 : args.length); + List constructorArgs = + resolveConstructorArguments(args, 0, hasClosureArgument ? args.length - 1 : args.length); this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null, constructorArgs); } if (hasClosureArgument) { - Closure callable = (Closure) args[args.length - 1]; + Closure callable = (Closure) args[args.length - 1]; callable.setDelegate(this); callable.setResolveStrategy(Closure.DELEGATE_FIRST); callable.call(this.currentBeanDefinition); @@ -549,7 +541,6 @@ else if (args[0] instanceof Closure) { return beanDefinition; } - @SuppressWarnings("rawtypes") protected List resolveConstructorArguments(Object[] args, int start, int end) { Object[] constructorArgs = Arrays.copyOfRange(args, start, end); for (int i = 0; i < constructorArgs.length; i++) { @@ -557,10 +548,10 @@ protected List resolveConstructorArguments(Object[] args, int start, int constructorArgs[i] = constructorArgs[i].toString(); } else if (constructorArgs[i] instanceof List) { - constructorArgs[i] = manageListIfNecessary((List) constructorArgs[i]); + constructorArgs[i] = manageListIfNecessary((List) constructorArgs[i]); } else if (constructorArgs[i] instanceof Map){ - constructorArgs[i] = manageMapIfNecessary((Map) constructorArgs[i]); + constructorArgs[i] = manageMapIfNecessary((Map) constructorArgs[i]); } } return Arrays.asList(constructorArgs); @@ -621,7 +612,6 @@ public void setProperty(String name, Object value) { } } - @SuppressWarnings("rawtypes") protected void applyPropertyToBeanDefinition(String name, Object value) { if (value instanceof GString) { value = value.toString(); @@ -632,7 +622,7 @@ protected void applyPropertyToBeanDefinition(String name, Object value) { else if (value instanceof Closure) { GroovyBeanDefinitionWrapper current = this.currentBeanDefinition; try { - Closure callable = (Closure) value; + Closure callable = (Closure) value; Class parameterType = callable.getParameterTypes()[0]; if (Object.class == parameterType) { this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(""); @@ -833,8 +823,8 @@ public boolean add(Object value) { return retVal; } - @SuppressWarnings({ "rawtypes", "unused" }) - public boolean addAll(Collection values) { + @SuppressWarnings("unused") + public boolean addAll(Collection values) { boolean retVal = (Boolean) InvokerHelper.invokeMethod(this.propertyValue, "addAll", values); for (Object value : values) { updateDeferredProperties(value); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionWrapper.java b/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionWrapper.java index 6e702036314a..d1aeff7d0bcc 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionWrapper.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -173,7 +173,6 @@ else if (dynamicProperties.contains(property)) { } @Override - @SuppressWarnings("rawtypes") public void setProperty(String property, Object newValue) { if (PARENT.equals(property)) { setParent(newValue); @@ -197,7 +196,7 @@ else if (Boolean.TRUE.equals(newValue)) { // constructorArgs else if (CONSTRUCTOR_ARGS.equals(property) && newValue instanceof List) { ConstructorArgumentValues cav = new ConstructorArgumentValues(); - List args = (List) newValue; + List args = (List) newValue; for (Object arg : args) { cav.addGenericArgumentValue(arg); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanEntry.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanEntry.java index 207650a27ba3..ccba6d76e688 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanEntry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanEntry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 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. @@ -24,11 +24,11 @@ */ public class BeanEntry implements ParseState.Entry { - private String beanDefinitionName; + private final String beanDefinitionName; /** - * Creates a new instance of {@link BeanEntry} class. + * Create a new {@code BeanEntry} instance. * @param beanDefinitionName the name of the associated bean definition */ public BeanEntry(String beanDefinitionName) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ParseState.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ParseState.java index 23a013ce91f1..0277bc0a0f9c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ParseState.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ParseState.java @@ -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. @@ -16,69 +16,63 @@ package org.springframework.beans.factory.parsing; -import java.util.LinkedList; +import java.util.ArrayDeque; import org.springframework.lang.Nullable; /** - * Simple {@link LinkedList}-based structure for tracking the logical position during - * a parsing process. {@link Entry entries} are added to the LinkedList at - * each point during the parse phase in a reader-specific manner. + * Simple {@link ArrayDeque}-based structure for tracking the logical position during + * a parsing process. {@link Entry entries} are added to the ArrayDeque at each point + * during the parse phase in a reader-specific manner. * *

Calling {@link #toString()} will render a tree-style view of the current logical - * position in the parse phase. This representation is intended for use in - * error messages. + * position in the parse phase. This representation is intended for use in error messages. * * @author Rob Harrop + * @author Juergen Hoeller * @since 2.0 */ public final class ParseState { /** - * Tab character used when rendering the tree-style representation. + * Internal {@link ArrayDeque} storage. */ - private static final char TAB = '\t'; - - /** - * Internal {@link LinkedList} storage. - */ - private final LinkedList state; + private final ArrayDeque state; /** - * Create a new {@code ParseState} with an empty {@link LinkedList}. + * Create a new {@code ParseState} with an empty {@link ArrayDeque}. */ public ParseState() { - this.state = new LinkedList<>(); + this.state = new ArrayDeque<>(); } /** - * Create a new {@code ParseState} whose {@link LinkedList} is a {@link Object#clone clone} - * of that of the passed in {@code ParseState}. + * Create a new {@code ParseState} whose {@link ArrayDeque} is a clone + * of the state in the passed-in {@code ParseState}. */ - @SuppressWarnings("unchecked") private ParseState(ParseState other) { - this.state = (LinkedList) other.state.clone(); + this.state = other.state.clone(); } /** - * Add a new {@link Entry} to the {@link LinkedList}. + * Add a new {@link Entry} to the {@link ArrayDeque}. */ public void push(Entry entry) { this.state.push(entry); } /** - * Remove an {@link Entry} from the {@link LinkedList}. + * Remove an {@link Entry} from the {@link ArrayDeque}. */ public void pop() { this.state.pop(); } /** - * Return the {@link Entry} currently at the top of the {@link LinkedList} or - * {@code null} if the {@link LinkedList} is empty. + * Return the {@link Entry} currently at the top of the {@link ArrayDeque} or + * {@code null} if the {@link ArrayDeque} is empty. */ @Nullable public Entry peek() { @@ -99,16 +93,18 @@ public ParseState snapshot() { */ @Override public String toString() { - StringBuilder sb = new StringBuilder(); - for (int x = 0; x < this.state.size(); x++) { - if (x > 0) { + StringBuilder sb = new StringBuilder(64); + int i = 0; + for (ParseState.Entry entry : this.state) { + if (i > 0) { sb.append('\n'); - for (int y = 0; y < x; y++) { - sb.append(TAB); + for (int j = 0; j < i; j++) { + sb.append('\t'); } sb.append("-> "); } - sb.append(this.state.get(x)); + sb.append(entry); + i++; } return sb.toString(); } @@ -118,7 +114,6 @@ public String toString() { * Marker interface for entries into the {@link ParseState}. */ public interface Entry { - } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/PropertyEntry.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/PropertyEntry.java index 983e72101b87..c20235a09b78 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/PropertyEntry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/PropertyEntry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 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,14 +30,12 @@ public class PropertyEntry implements ParseState.Entry { /** - * Creates a new instance of the {@link PropertyEntry} class. + * Create a new {@code PropertyEntry} instance. * @param name the name of the JavaBean property represented by this instance - * @throws IllegalArgumentException if the supplied {@code name} is {@code null} - * or consists wholly of whitespace */ public PropertyEntry(String name) { if (!StringUtils.hasText(name)) { - throw new IllegalArgumentException("Invalid property name '" + name + "'."); + throw new IllegalArgumentException("Invalid property name '" + name + "'"); } this.name = name; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/QualifierEntry.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/QualifierEntry.java index 8fc3207e80ec..45283e5838ce 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/QualifierEntry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/QualifierEntry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 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. @@ -26,16 +26,21 @@ */ public class QualifierEntry implements ParseState.Entry { - private String typeName; + private final String typeName; + /** + * Create a new {@code QualifierEntry} instance. + * @param typeName the name of the qualifier type + */ public QualifierEntry(String typeName) { if (!StringUtils.hasText(typeName)) { - throw new IllegalArgumentException("Invalid qualifier type '" + typeName + "'."); + throw new IllegalArgumentException("Invalid qualifier type '" + typeName + "'"); } this.typeName = typeName; } + @Override public String toString() { return "Qualifier '" + this.typeName + "'"; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ReaderContext.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ReaderContext.java index 7c31fe994b7d..2b95aa8f6c3b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ReaderContext.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ReaderContext.java @@ -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. @@ -152,14 +152,14 @@ public void warning(String message, @Nullable Object source, @Nullable ParseStat // Explicit parse events /** - * Fire an defaults-registered event. + * Fire a defaults-registered event. */ public void fireDefaultsRegistered(DefaultsDefinition defaultsDefinition) { this.eventListener.defaultsRegistered(defaultsDefinition); } /** - * Fire an component-registered event. + * Fire a component-registered event. */ public void fireComponentRegistered(ComponentDefinition componentDefinition) { this.eventListener.componentRegistered(componentDefinition); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/serviceloader/ServiceListFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/serviceloader/ServiceListFactoryBean.java index b18883bab776..6e97d2f29c82 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/serviceloader/ServiceListFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/serviceloader/ServiceListFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 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,7 +16,7 @@ package org.springframework.beans.factory.serviceloader; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.ServiceLoader; @@ -35,7 +35,7 @@ public class ServiceListFactoryBean extends AbstractServiceLoaderBasedFactoryBea @Override protected Object getObjectToExpose(ServiceLoader serviceLoader) { - List result = new LinkedList<>(); + List result = new ArrayList<>(); for (Object loaderObject : serviceLoader) { result.add(loaderObject); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index 35d0df235c27..ff76dcdd3710 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -73,6 +73,7 @@ import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.MethodParameter; import org.springframework.core.NamedThreadLocal; +import org.springframework.core.NativeDetector; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.PriorityOrdered; import org.springframework.core.ResolvableType; @@ -124,7 +125,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac implements AutowireCapableBeanFactory { /** Strategy for creating bean instances. */ - private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy(); + private InstantiationStrategy instantiationStrategy; /** Resolver strategy for method parameter names. */ @Nullable @@ -176,6 +177,12 @@ public AbstractAutowireCapableBeanFactory() { ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); + if (NativeDetector.inNativeImage()) { + this.instantiationStrategy = new SimpleInstantiationStrategy(); + } + else { + this.instantiationStrategy = new CglibSubclassingInstantiationStrategy(); + } } /** @@ -311,7 +318,7 @@ public T createBean(Class beanClass) throws BeansException { public void autowireBean(Object existingBean) { // Use non-singleton bean definition, to avoid registering bean as dependent bean. RootBeanDefinition bd = new RootBeanDefinition(ClassUtils.getUserClass(existingBean)); - bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(SCOPE_PROTOTYPE); bd.allowCaching = ClassUtils.isCacheSafe(bd.getBeanClass(), getBeanClassLoader()); BeanWrapper bw = new BeanWrapperImpl(existingBean); initBeanWrapper(bw); @@ -331,7 +338,7 @@ public Object configureBean(Object existingBean, String beanName) throws BeansEx bd = new RootBeanDefinition(mbd); } if (!bd.isPrototype()) { - bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(SCOPE_PROTOTYPE); bd.allowCaching = ClassUtils.isCacheSafe(ClassUtils.getUserClass(existingBean), getBeanClassLoader()); } BeanWrapper bw = new BeanWrapperImpl(existingBean); @@ -349,28 +356,27 @@ public Object configureBean(Object existingBean, String beanName) throws BeansEx public Object createBean(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException { // Use non-singleton bean definition, to avoid registering bean as dependent bean. RootBeanDefinition bd = new RootBeanDefinition(beanClass, autowireMode, dependencyCheck); - bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(SCOPE_PROTOTYPE); return createBean(beanClass.getName(), bd, null); } @Override public Object autowire(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException { // Use non-singleton bean definition, to avoid registering bean as dependent bean. - final RootBeanDefinition bd = new RootBeanDefinition(beanClass, autowireMode, dependencyCheck); - bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); + RootBeanDefinition bd = new RootBeanDefinition(beanClass, autowireMode, dependencyCheck); + bd.setScope(SCOPE_PROTOTYPE); if (bd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR) { return autowireConstructor(beanClass.getName(), bd, null, null).getWrappedInstance(); } else { Object bean; - final BeanFactory parent = this; if (System.getSecurityManager() != null) { - bean = AccessController.doPrivileged((PrivilegedAction) () -> - getInstantiationStrategy().instantiate(bd, null, parent), + bean = AccessController.doPrivileged( + (PrivilegedAction) () -> getInstantiationStrategy().instantiate(bd, null, this), getAccessControlContext()); } else { - bean = getInstantiationStrategy().instantiate(bd, null, parent); + bean = getInstantiationStrategy().instantiate(bd, null, this); } populateBean(beanClass.getName(), bd, new BeanWrapperImpl(bean)); return bean; @@ -387,7 +393,7 @@ public void autowireBeanProperties(Object existingBean, int autowireMode, boolea // Use non-singleton bean definition, to avoid registering bean as dependent bean. RootBeanDefinition bd = new RootBeanDefinition(ClassUtils.getUserClass(existingBean), autowireMode, dependencyCheck); - bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(SCOPE_PROTOTYPE); BeanWrapper bw = new BeanWrapperImpl(existingBean); initBeanWrapper(bw); populateBean(bd.getBeanClass().getName(), bd, bw); @@ -439,7 +445,8 @@ public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, St @Override public void destroyBean(Object existingBean) { - new DisposableBeanAdapter(existingBean, getBeanPostProcessors(), getAccessControlContext()).destroy(); + new DisposableBeanAdapter( + existingBean, getBeanPostProcessorCache().destructionAware, getAccessControlContext()).destroy(); } @@ -545,7 +552,7 @@ protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable O * @see #instantiateUsingFactoryMethod * @see #autowireConstructor */ - protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) + protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. @@ -556,7 +563,7 @@ protected Object doCreateBean(final String beanName, final RootBeanDefinition mb if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } - final Object bean = instanceWrapper.getWrappedInstance(); + Object bean = instanceWrapper.getWrappedInstance(); Class beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; @@ -625,7 +632,7 @@ else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + - "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); + "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); } } } @@ -651,14 +658,11 @@ protected Class predictBeanType(String beanName, RootBeanDefinition mbd, Clas // eventual type after a before-instantiation shortcut. if (targetType != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { boolean matchingOnlyFactoryBean = typesToMatch.length == 1 && typesToMatch[0] == FactoryBean.class; - for (BeanPostProcessor bp : getBeanPostProcessors()) { - if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { - SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; - Class predicted = ibp.predictBeanType(targetType, beanName); - if (predicted != null && - (!matchingOnlyFactoryBean || FactoryBean.class.isAssignableFrom(predicted))) { - return predicted; - } + for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { + Class predicted = bp.predictBeanType(targetType, beanName); + if (predicted != null && + (!matchingOnlyFactoryBean || FactoryBean.class.isAssignableFrom(predicted))) { + return predicted; } } } @@ -959,11 +963,8 @@ protected Class getTypeForFactoryBean(String beanName, RootBeanDefinition mbd protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { - for (BeanPostProcessor bp : getBeanPostProcessors()) { - if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { - SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; - exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); - } + for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { + exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); } } return exposedObject; @@ -1014,6 +1015,11 @@ private FactoryBean getSingletonFactoryBeanForTypeCheck(String beanName, Root throw ex; } catch (BeanCreationException ex) { + // Don't swallow a linkage error since it contains a full stacktrace on + // first occurrence... and just a plain NoClassDefFoundError afterwards. + if (ex.contains(LinkageError.class)) { + throw ex; + } // Instantiation failure, maybe too early... if (logger.isDebugEnabled()) { logger.debug("Bean creation exception on singleton FactoryBean type check: " + ex); @@ -1088,11 +1094,8 @@ private FactoryBean getNonSingletonFactoryBeanForTypeCheck(String beanName, R * @see MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition */ protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class beanType, String beanName) { - for (BeanPostProcessor bp : getBeanPostProcessors()) { - if (bp instanceof MergedBeanDefinitionPostProcessor) { - MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp; - bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName); - } + for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) { + processor.postProcessMergedBeanDefinition(mbd, beanType, beanName); } } @@ -1135,13 +1138,10 @@ protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition */ @Nullable protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName) { - for (BeanPostProcessor bp : getBeanPostProcessors()) { - if (bp instanceof InstantiationAwareBeanPostProcessor) { - InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; - Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName); - if (result != null) { - return result; - } + for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { + Object result = bp.postProcessBeforeInstantiation(beanClass, beanName); + if (result != null) { + return result; } } return null; @@ -1280,13 +1280,10 @@ protected Constructor[] determineConstructorsFromBeanPostProcessors(@Nullable throws BeansException { if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) { - for (BeanPostProcessor bp : getBeanPostProcessors()) { - if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { - SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; - Constructor[] ctors = ibp.determineCandidateConstructors(beanClass, beanName); - if (ctors != null) { - return ctors; - } + for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { + Constructor[] ctors = bp.determineCandidateConstructors(beanClass, beanName); + if (ctors != null) { + return ctors; } } } @@ -1299,17 +1296,16 @@ protected Constructor[] determineConstructorsFromBeanPostProcessors(@Nullable * @param mbd the bean definition for the bean * @return a BeanWrapper for the new instance */ - protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { + protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) { try { Object beanInstance; - final BeanFactory parent = this; if (System.getSecurityManager() != null) { - beanInstance = AccessController.doPrivileged((PrivilegedAction) () -> - getInstantiationStrategy().instantiate(mbd, beanName, parent), + beanInstance = AccessController.doPrivileged( + (PrivilegedAction) () -> getInstantiationStrategy().instantiate(mbd, beanName, this), getAccessControlContext()); } else { - beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); + beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this); } BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); @@ -1381,24 +1377,14 @@ protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable B // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the // state of the bean before properties are set. This can be used, for example, // to support styles of field injection. - boolean continueWithPropertyPopulation = true; - if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { - for (BeanPostProcessor bp : getBeanPostProcessors()) { - if (bp instanceof InstantiationAwareBeanPostProcessor) { - InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; - if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { - continueWithPropertyPopulation = false; - break; - } + for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { + if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { + return; } } } - if (!continueWithPropertyPopulation) { - return; - } - PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); int resolvedAutowireMode = mbd.getResolvedAutowireMode(); @@ -1423,21 +1409,18 @@ protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable B if (pvs == null) { pvs = mbd.getPropertyValues(); } - for (BeanPostProcessor bp : getBeanPostProcessors()) { - if (bp instanceof InstantiationAwareBeanPostProcessor) { - InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; - PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); + for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { + PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); + if (pvsToUse == null) { + if (filteredPds == null) { + filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); + } + pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { - if (filteredPds == null) { - filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); - } - pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); - if (pvsToUse == null) { - return; - } + return; } - pvs = pvsToUse; } + pvs = pvsToUse; } } if (needsDepCheck) { @@ -1513,7 +1496,7 @@ protected void autowireByType( if (Object.class != pd.getPropertyType()) { MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); // Do not allow eager init for type matching in case of a prioritized post-processor. - boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance()); + boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered); DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter); if (autowiredArgument != null) { @@ -1779,7 +1762,7 @@ private Object convertForProperty( * @see #invokeInitMethods * @see #applyBeanPostProcessorsAfterInitialization */ - protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { + protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction) () -> { invokeAwareMethods(beanName, bean); @@ -1810,7 +1793,7 @@ protected Object initializeBean(final String beanName, final Object bean, @Nulla return wrappedBean; } - private void invokeAwareMethods(final String beanName, final Object bean) { + private void invokeAwareMethods(String beanName, Object bean) { if (bean instanceof Aware) { if (bean instanceof BeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); @@ -1839,7 +1822,7 @@ private void invokeAwareMethods(final String beanName, final Object bean) { * @throws Throwable if thrown by init methods or by the invocation process * @see #invokeCustomInitMethod */ - protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) + protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = (bean instanceof InitializingBean); @@ -1880,7 +1863,7 @@ protected void invokeInitMethods(String beanName, final Object bean, @Nullable R * methods with arguments. * @see #invokeInitMethods */ - protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd) + protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition mbd) throws Throwable { String initMethodName = mbd.getInitMethodName(); @@ -1915,8 +1898,8 @@ protected void invokeCustomInitMethod(String beanName, final Object bean, RootBe return null; }); try { - AccessController.doPrivileged((PrivilegedExceptionAction) () -> - methodToInvoke.invoke(bean), getAccessControlContext()); + AccessController.doPrivileged((PrivilegedExceptionAction) + () -> methodToInvoke.invoke(bean), getAccessControlContext()); } catch (PrivilegedActionException pae) { InvocationTargetException ex = (InvocationTargetException) pae.getException(); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java index b1c6691d0968..5b5471a88323 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -403,16 +403,29 @@ public String getBeanClassName() { /** * Specify the class for this bean. + * @see #setBeanClassName(String) */ public void setBeanClass(@Nullable Class beanClass) { this.beanClass = beanClass; } /** - * Return the class of the wrapped bean (assuming it is resolved already). - * @return the bean class (never {@code null}) + * Return the specified class of the bean definition (assuming it is resolved already). + *

NOTE: This is an initial class reference as declared in the bean metadata + * definition, potentially combined with a declared factory method or a + * {@link org.springframework.beans.factory.FactoryBean} which may lead to a different + * runtime type of the bean, or not being set at all in case of an instance-level + * factory method (which is resolved via {@link #getFactoryBeanName()} instead). + * Do not use this for runtime type introspection of arbitrary bean definitions. + * The recommended way to find out about the actual runtime type of a particular bean + * is a {@link org.springframework.beans.factory.BeanFactory#getType} call for the + * specified bean name; this takes all of the above cases into account and returns the + * type of object that a {@link org.springframework.beans.factory.BeanFactory#getBean} + * call is going to return for the same bean name. + * @return the resolved bean class (never {@code null}) * @throws IllegalStateException if the bean definition does not define a bean class, * or a specified bean class name has not been resolved into an actual Class yet + * @see #getBeanClassName() * @see #hasBeanClass() * @see #setBeanClass(Class) * @see #resolveBeanClass(ClassLoader) @@ -881,7 +894,7 @@ public MutablePropertyValues getPropertyValues() { } /** - * Return if there are property values values defined for this bean. + * Return if there are property values defined for this bean. * @since 5.0.2 */ @Override @@ -932,16 +945,20 @@ public String getInitMethodName() { } /** - * Specify whether or not the configured init method is the default. - *

The default value is {@code false}. + * Specify whether or not the configured initializer method is the default. + *

The default value is {@code true} for a locally specified init method + * but switched to {@code false} for a shared setting in a defaults section + * (e.g. {@code bean init-method} versus {@code beans default-init-method} + * level in XML) which might not apply to all contained bean definitions. * @see #setInitMethodName + * @see #applyDefaults */ public void setEnforceInitMethod(boolean enforceInitMethod) { this.enforceInitMethod = enforceInitMethod; } /** - * Indicate whether the configured init method is the default. + * Indicate whether the configured initializer method is the default. * @see #getInitMethodName() */ public boolean isEnforceInitMethod() { @@ -968,8 +985,12 @@ public String getDestroyMethodName() { /** * Specify whether or not the configured destroy method is the default. - *

The default value is {@code false}. + *

The default value is {@code true} for a locally specified destroy method + * but switched to {@code false} for a shared setting in a defaults section + * (e.g. {@code bean destroy-method} versus {@code beans default-destroy-method} + * level in XML) which might not apply to all contained bean definitions. * @see #setDestroyMethodName + * @see #applyDefaults */ public void setEnforceDestroyMethod(boolean enforceDestroyMethod) { this.enforceDestroyMethod = enforceDestroyMethod; @@ -977,7 +998,7 @@ public void setEnforceDestroyMethod(boolean enforceDestroyMethod) { /** * Indicate whether the configured destroy method is the default. - * @see #getDestroyMethodName + * @see #getDestroyMethodName() */ public boolean isEnforceDestroyMethod() { return this.enforceDestroyMethod; @@ -1161,38 +1182,56 @@ public boolean equals(@Nullable Object other) { return false; } AbstractBeanDefinition that = (AbstractBeanDefinition) other; - boolean rtn = ObjectUtils.nullSafeEquals(getBeanClassName(), that.getBeanClassName()); - rtn = rtn &= ObjectUtils.nullSafeEquals(this.scope, that.scope); - rtn = rtn &= this.abstractFlag == that.abstractFlag; - rtn = rtn &= this.lazyInit == that.lazyInit; - rtn = rtn &= this.autowireMode == that.autowireMode; - rtn = rtn &= this.dependencyCheck == that.dependencyCheck; - rtn = rtn &= Arrays.equals(this.dependsOn, that.dependsOn); - rtn = rtn &= this.autowireCandidate == that.autowireCandidate; - rtn = rtn &= ObjectUtils.nullSafeEquals(this.qualifiers, that.qualifiers); - rtn = rtn &= this.primary == that.primary; - rtn = rtn &= this.nonPublicAccessAllowed == that.nonPublicAccessAllowed; - rtn = rtn &= this.lenientConstructorResolution == that.lenientConstructorResolution; - rtn = rtn &= ObjectUtils.nullSafeEquals(this.constructorArgumentValues, that.constructorArgumentValues); - rtn = rtn &= ObjectUtils.nullSafeEquals(this.propertyValues, that.propertyValues); - rtn = rtn &= ObjectUtils.nullSafeEquals(this.methodOverrides, that.methodOverrides); - rtn = rtn &= ObjectUtils.nullSafeEquals(this.factoryBeanName, that.factoryBeanName); - rtn = rtn &= ObjectUtils.nullSafeEquals(this.factoryMethodName, that.factoryMethodName); - rtn = rtn &= ObjectUtils.nullSafeEquals(this.initMethodName, that.initMethodName); - rtn = rtn &= this.enforceInitMethod == that.enforceInitMethod; - rtn = rtn &= ObjectUtils.nullSafeEquals(this.destroyMethodName, that.destroyMethodName); - rtn = rtn &= this.enforceDestroyMethod == that.enforceDestroyMethod; - rtn = rtn &= this.synthetic == that.synthetic; - rtn = rtn &= this.role == that.role; - return rtn && super.equals(other); + return (ObjectUtils.nullSafeEquals(getBeanClassName(), that.getBeanClassName()) && + ObjectUtils.nullSafeEquals(this.scope, that.scope) && + this.abstractFlag == that.abstractFlag && + this.lazyInit == that.lazyInit && + this.autowireMode == that.autowireMode && + this.dependencyCheck == that.dependencyCheck && + Arrays.equals(this.dependsOn, that.dependsOn) && + this.autowireCandidate == that.autowireCandidate && + ObjectUtils.nullSafeEquals(this.qualifiers, that.qualifiers) && + this.primary == that.primary && + this.nonPublicAccessAllowed == that.nonPublicAccessAllowed && + this.lenientConstructorResolution == that.lenientConstructorResolution && + equalsConstructorArgumentValues(that) && + equalsPropertyValues(that) && + ObjectUtils.nullSafeEquals(this.methodOverrides, that.methodOverrides) && + ObjectUtils.nullSafeEquals(this.factoryBeanName, that.factoryBeanName) && + ObjectUtils.nullSafeEquals(this.factoryMethodName, that.factoryMethodName) && + ObjectUtils.nullSafeEquals(this.initMethodName, that.initMethodName) && + this.enforceInitMethod == that.enforceInitMethod && + ObjectUtils.nullSafeEquals(this.destroyMethodName, that.destroyMethodName) && + this.enforceDestroyMethod == that.enforceDestroyMethod && + this.synthetic == that.synthetic && + this.role == that.role && + super.equals(other)); + } + + private boolean equalsConstructorArgumentValues(AbstractBeanDefinition other) { + if (!hasConstructorArgumentValues()) { + return !other.hasConstructorArgumentValues(); + } + return ObjectUtils.nullSafeEquals(this.constructorArgumentValues, other.constructorArgumentValues); + } + + private boolean equalsPropertyValues(AbstractBeanDefinition other) { + if (!hasPropertyValues()) { + return !other.hasPropertyValues(); + } + return ObjectUtils.nullSafeEquals(this.propertyValues, other.propertyValues); } @Override public int hashCode() { int hashCode = ObjectUtils.nullSafeHashCode(getBeanClassName()); hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.scope); - hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.constructorArgumentValues); - hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.propertyValues); + if (hasConstructorArgumentValues()) { + hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.constructorArgumentValues); + } + if (hasPropertyValues()) { + hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.propertyValues); + } hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.factoryBeanName); hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.factoryMethodName); hashCode = 29 * hashCode + super.hashCode(); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index 29fe3a8c4649..562c00fd55f1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -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. @@ -24,6 +24,7 @@ import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -34,6 +35,8 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanWrapper; @@ -65,12 +68,15 @@ import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.config.Scope; +import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; import org.springframework.core.AttributeAccessor; import org.springframework.core.DecoratingClassLoader; import org.springframework.core.NamedThreadLocal; import org.springframework.core.ResolvableType; import org.springframework.core.convert.ConversionService; import org.springframework.core.log.LogMessage; +import org.springframework.core.metrics.ApplicationStartup; +import org.springframework.core.metrics.StartupStep; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -150,14 +156,12 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp /** String resolvers to apply e.g. to annotation attribute values. */ private final List embeddedValueResolvers = new CopyOnWriteArrayList<>(); - /** BeanPostProcessors to apply in createBean. */ - private final List beanPostProcessors = new CopyOnWriteArrayList<>(); + /** BeanPostProcessors to apply. */ + private final List beanPostProcessors = new BeanPostProcessorCacheAwareList(); - /** Indicates whether any InstantiationAwareBeanPostProcessors have been registered. */ - private volatile boolean hasInstantiationAwareBeanPostProcessors; - - /** Indicates whether any DestructionAwareBeanPostProcessors have been registered. */ - private volatile boolean hasDestructionAwareBeanPostProcessors; + /** Cache of pre-filtered post-processors. */ + @Nullable + private volatile BeanPostProcessorCache beanPostProcessorCache; /** Map from scope identifier String to corresponding Scope. */ private final Map scopes = new LinkedHashMap<>(8); @@ -176,6 +180,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp private final ThreadLocal prototypesCurrentlyInCreation = new NamedThreadLocal<>("Prototype beans currently in creation"); + /** Application startup metrics. **/ + private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT; /** * Create a new AbstractBeanFactory. @@ -239,11 +245,12 @@ public T getBean(String name, @Nullable Class requiredType, @Nullable Obj * @throws BeansException if the bean could not be created */ @SuppressWarnings("unchecked") - protected T doGetBean(final String name, @Nullable final Class requiredType, - @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { + protected T doGetBean( + String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) + throws BeansException { - final String beanName = transformedBeanName(name); - Object bean; + String beanName = transformedBeanName(name); + Object beanInstance; // Eagerly check singleton cache for manually registered singletons. Object sharedInstance = getSingleton(beanName); @@ -257,7 +264,7 @@ protected T doGetBean(final String name, @Nullable final Class requiredTy logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); } } - bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { @@ -293,8 +300,13 @@ else if (requiredType != null) { markBeanAsCreated(beanName); } + StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate") + .tag("beanName", name); try { - final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); + if (requiredType != null) { + beanCreation.tag("beanType", requiredType::toString); + } + RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. @@ -330,7 +342,7 @@ else if (requiredType != null) { throw ex; } }); - bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); + beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { @@ -343,12 +355,15 @@ else if (mbd.isPrototype()) { finally { afterPrototypeCreation(beanName); } - bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); + beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { String scopeName = mbd.getScope(); - final Scope scope = this.scopes.get(scopeName); + if (!StringUtils.hasLength(scopeName)) { + throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'"); + } + Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } @@ -362,30 +377,37 @@ else if (mbd.isPrototype()) { afterPrototypeCreation(beanName); } }); - bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); + beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { - throw new BeanCreationException(beanName, - "Scope '" + scopeName + "' is not active for the current thread; consider " + - "defining a scoped proxy for this bean if you intend to refer to it from a singleton", - ex); + throw new ScopeNotActiveException(beanName, scopeName, ex); } } } catch (BeansException ex) { + beanCreation.tag("exception", ex.getClass().toString()); + beanCreation.tag("message", String.valueOf(ex.getMessage())); cleanupAfterBeanCreationFailure(beanName); throw ex; } + finally { + beanCreation.end(); + } } + return adaptBeanInstance(name, beanInstance, requiredType); + } + + @SuppressWarnings("unchecked") + T adaptBeanInstance(String name, Object bean, @Nullable Class requiredType) { // Check if required type matches the type of the actual bean instance. if (requiredType != null && !requiredType.isInstance(bean)) { try { - T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); + Object convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); if (convertedBean == null) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } - return convertedBean; + return (T) convertedBean; } catch (TypeMismatchException ex) { if (logger.isTraceEnabled()) { @@ -472,10 +494,12 @@ public boolean isPrototype(String name) throws NoSuchBeanDefinitionException { return false; } if (isFactoryBean(beanName, mbd)) { - final FactoryBean fb = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName); + FactoryBean fb = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName); if (System.getSecurityManager() != null) { - return AccessController.doPrivileged((PrivilegedAction) () -> - ((fb instanceof SmartFactoryBean && ((SmartFactoryBean) fb).isPrototype()) || !fb.isSingleton()), + return AccessController.doPrivileged( + (PrivilegedAction) () -> + ((fb instanceof SmartFactoryBean && ((SmartFactoryBean) fb).isPrototype()) || + !fb.isSingleton()), getAccessControlContext()); } else { @@ -725,8 +749,9 @@ public String[] getAliases(String name) { aliases.add(fullBeanName); } String[] retrievedAliases = super.getAliases(beanName); + String prefix = factoryPrefix ? FACTORY_BEAN_PREFIX : ""; for (String retrievedAlias : retrievedAliases) { - String alias = (factoryPrefix ? FACTORY_BEAN_PREFIX : "") + retrievedAlias; + String alias = prefix + retrievedAlias; if (!alias.equals(name)) { aliases.add(alias); } @@ -768,6 +793,9 @@ public void setParentBeanFactory(@Nullable BeanFactory parentBeanFactory) { if (this.parentBeanFactory != null && this.parentBeanFactory != parentBeanFactory) { throw new IllegalStateException("Already associated with parent BeanFactory: " + this.parentBeanFactory); } + if (this == parentBeanFactory) { + throw new IllegalStateException("Cannot set parent bean factory to self"); + } this.parentBeanFactory = parentBeanFactory; } @@ -918,17 +946,21 @@ public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) { Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null"); // Remove from old position, if any this.beanPostProcessors.remove(beanPostProcessor); - // Track whether it is instantiation/destruction aware - if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) { - this.hasInstantiationAwareBeanPostProcessors = true; - } - if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) { - this.hasDestructionAwareBeanPostProcessors = true; - } // Add to end of list this.beanPostProcessors.add(beanPostProcessor); } + /** + * Add new BeanPostProcessors that will get applied to beans created + * by this factory. To be invoked during factory configuration. + * @since 5.3 + * @see #addBeanPostProcessor + */ + public void addBeanPostProcessors(Collection beanPostProcessors) { + this.beanPostProcessors.removeAll(beanPostProcessors); + this.beanPostProcessors.addAll(beanPostProcessors); + } + @Override public int getBeanPostProcessorCount() { return this.beanPostProcessors.size(); @@ -942,14 +974,42 @@ public List getBeanPostProcessors() { return this.beanPostProcessors; } + /** + * Return the internal cache of pre-filtered post-processors, + * freshly (re-)building it if necessary. + * @since 5.3 + */ + BeanPostProcessorCache getBeanPostProcessorCache() { + BeanPostProcessorCache bpCache = this.beanPostProcessorCache; + if (bpCache == null) { + bpCache = new BeanPostProcessorCache(); + for (BeanPostProcessor bp : this.beanPostProcessors) { + if (bp instanceof InstantiationAwareBeanPostProcessor) { + bpCache.instantiationAware.add((InstantiationAwareBeanPostProcessor) bp); + if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { + bpCache.smartInstantiationAware.add((SmartInstantiationAwareBeanPostProcessor) bp); + } + } + if (bp instanceof DestructionAwareBeanPostProcessor) { + bpCache.destructionAware.add((DestructionAwareBeanPostProcessor) bp); + } + if (bp instanceof MergedBeanDefinitionPostProcessor) { + bpCache.mergedDefinition.add((MergedBeanDefinitionPostProcessor) bp); + } + } + this.beanPostProcessorCache = bpCache; + } + return bpCache; + } + /** * Return whether this factory holds a InstantiationAwareBeanPostProcessor - * that will get applied to singleton beans on shutdown. + * that will get applied to singleton beans on creation. * @see #addBeanPostProcessor * @see org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor */ protected boolean hasInstantiationAwareBeanPostProcessors() { - return this.hasInstantiationAwareBeanPostProcessors; + return !getBeanPostProcessorCache().instantiationAware.isEmpty(); } /** @@ -959,7 +1019,7 @@ protected boolean hasInstantiationAwareBeanPostProcessors() { * @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor */ protected boolean hasDestructionAwareBeanPostProcessors() { - return this.hasDestructionAwareBeanPostProcessors; + return !getBeanPostProcessorCache().destructionAware.isEmpty(); } @Override @@ -1003,6 +1063,17 @@ public void setSecurityContextProvider(SecurityContextProvider securityProvider) this.securityContextProvider = securityProvider; } + @Override + public void setApplicationStartup(ApplicationStartup applicationStartup) { + Assert.notNull(applicationStartup, "applicationStartup should not be null"); + this.applicationStartup = applicationStartup; + } + + @Override + public ApplicationStartup getApplicationStartup() { + return this.applicationStartup; + } + /** * Delegate the creation of the access control context to the * {@link #setSecurityContextProvider SecurityContextProvider}. @@ -1027,10 +1098,6 @@ public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) { this.customEditors.putAll(otherAbstractFactory.customEditors); this.typeConverter = otherAbstractFactory.typeConverter; this.beanPostProcessors.addAll(otherAbstractFactory.beanPostProcessors); - this.hasInstantiationAwareBeanPostProcessors = this.hasInstantiationAwareBeanPostProcessors || - otherAbstractFactory.hasInstantiationAwareBeanPostProcessors; - this.hasDestructionAwareBeanPostProcessors = this.hasDestructionAwareBeanPostProcessors || - otherAbstractFactory.hasDestructionAwareBeanPostProcessors; this.scopes.putAll(otherAbstractFactory.scopes); this.securityContextProvider = otherAbstractFactory.securityContextProvider; } @@ -1154,7 +1221,8 @@ public void destroyBean(String beanName, Object beanInstance) { * @param mbd the merged bean definition */ protected void destroyBean(String beanName, Object bean, RootBeanDefinition mbd) { - new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), getAccessControlContext()).destroy(); + new DisposableBeanAdapter( + bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, getAccessControlContext()).destroy(); } @Override @@ -1225,10 +1293,8 @@ protected void initBeanWrapper(BeanWrapper bw) { * @param registry the PropertyEditorRegistry to initialize */ protected void registerCustomEditors(PropertyEditorRegistry registry) { - PropertyEditorRegistrySupport registrySupport = - (registry instanceof PropertyEditorRegistrySupport ? (PropertyEditorRegistrySupport) registry : null); - if (registrySupport != null) { - registrySupport.useConfigValueEditors(); + if (registry instanceof PropertyEditorRegistrySupport) { + ((PropertyEditorRegistrySupport) registry).useConfigValueEditors(); } if (!this.propertyEditorRegistrars.isEmpty()) { for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) { @@ -1317,7 +1383,6 @@ protected RootBeanDefinition getMergedBeanDefinition( if (mbd == null || mbd.stale) { previous = mbd; - mbd = null; if (bd.getParentName() == null) { // Use copy of given root bean definition. if (bd instanceof RootBeanDefinition) { @@ -1343,7 +1408,7 @@ protected RootBeanDefinition getMergedBeanDefinition( else { throw new NoSuchBeanDefinitionException(parentBeanName, "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName + - "': cannot be resolved without an AbstractBeanFactory parent"); + "': cannot be resolved without a ConfigurableBeanFactory parent"); } } } @@ -1358,7 +1423,7 @@ protected RootBeanDefinition getMergedBeanDefinition( // Set default singleton scope, if not configured before. if (!StringUtils.hasLength(mbd.getScope())) { - mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON); + mbd.setScope(SCOPE_SINGLETON); } // A bean contained in a non-singleton bean cannot be a singleton itself. @@ -1454,7 +1519,7 @@ public void clearMetadataCache() { * @throws CannotLoadBeanClassException if we failed to load the class */ @Nullable - protected Class resolveBeanClass(final RootBeanDefinition mbd, String beanName, final Class... typesToMatch) + protected Class resolveBeanClass(RootBeanDefinition mbd, String beanName, Class... typesToMatch) throws CannotLoadBeanClassException { try { @@ -1462,8 +1527,8 @@ protected Class resolveBeanClass(final RootBeanDefinition mbd, String beanNam return mbd.getBeanClass(); } if (System.getSecurityManager() != null) { - return AccessController.doPrivileged((PrivilegedExceptionAction>) () -> - doResolveBeanClass(mbd, typesToMatch), getAccessControlContext()); + return AccessController.doPrivileged((PrivilegedExceptionAction>) + () -> doResolveBeanClass(mbd, typesToMatch), getAccessControlContext()); } else { return doResolveBeanClass(mbd, typesToMatch); @@ -1612,21 +1677,22 @@ protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) { /** * Determine the bean type for the given FactoryBean definition, as far as possible. * Only called if there is no singleton instance registered for the target bean - * already. Implementations are only allowed to instantiate the factory bean if - * {@code allowInit} is {@code true}, otherwise they should try to determine the - * result through other means. + * already. The implementation is allowed to instantiate the target factory bean if + * {@code allowInit} is {@code true} and the type cannot be determined another way; + * otherwise it is restricted to introspecting signatures and related metadata. *

If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} if set on the bean definition * and {@code allowInit} is {@code true}, the default implementation will create * the FactoryBean via {@code getBean} to call its {@code getObjectType} method. * Subclasses are encouraged to optimize this, typically by inspecting the generic - * signature of the factory bean class or the factory method that creates it. If - * subclasses do instantiate the FactoryBean, they should consider trying the - * {@code getObjectType} method without fully populating the bean. If this fails, a - * full FactoryBean creation as performed by this implementation should be used as - * fallback. + * signature of the factory bean class or the factory method that creates it. + * If subclasses do instantiate the FactoryBean, they should consider trying the + * {@code getObjectType} method without fully populating the bean. If this fails, + * a full FactoryBean creation as performed by this implementation should be used + * as fallback. * @param beanName the name of the bean * @param mbd the merged bean definition for the bean - * @param allowInit if initialization of the FactoryBean is permitted + * @param allowInit if initialization of the FactoryBean is permitted if the type + * cannot be determined another way * @return the type for the bean if determinable, otherwise {@code ResolvableType.NONE} * @since 5.2 * @see org.springframework.beans.factory.FactoryBean#getObjectType() @@ -1642,7 +1708,7 @@ protected ResolvableType getTypeForFactoryBean(String beanName, RootBeanDefiniti try { FactoryBean factoryBean = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true); Class objectType = getTypeForFactoryBean(factoryBean); - return (objectType != null) ? ResolvableType.forClass(objectType) : ResolvableType.NONE; + return (objectType != null ? ResolvableType.forClass(objectType) : ResolvableType.NONE); } catch (BeanCreationException ex) { if (ex.contains(BeanCurrentlyInCreationException.class)) { @@ -1652,7 +1718,7 @@ else if (mbd.isLazyInit()) { logger.trace(LogMessage.format("Bean creation exception on lazy FactoryBean type check: %s", ex)); } else { - logger.debug(LogMessage.format("Bean creation exception on non-lazy FactoryBean type check: %s", ex)); + logger.debug(LogMessage.format("Bean creation exception on eager FactoryBean type check: %s", ex)); } onSuppressedException(ex); } @@ -1771,7 +1837,7 @@ protected boolean hasBeanCreationStarted() { * Get the object for the given bean instance, either the bean * instance itself or its created object in case of a FactoryBean. * @param beanInstance the shared bean instance - * @param name name that may include factory dereference prefix + * @param name the name that may include factory dereference prefix * @param beanName the canonical bean name * @param mbd the merged bean definition * @return the object to expose for the bean @@ -1841,9 +1907,9 @@ public boolean isBeanNameInUse(String beanName) { * @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor */ protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) { - return (bean.getClass() != NullBean.class && - (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) || (hasDestructionAwareBeanPostProcessors() && - DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessors())))); + return (bean.getClass() != NullBean.class && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) || + (hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors( + bean, getBeanPostProcessorCache().destructionAware)))); } /** @@ -1865,8 +1931,8 @@ protected void registerDisposableBeanIfNecessary(String beanName, Object bean, R // Register a DisposableBean implementation that performs all destruction // work for the given bean: DestructionAwareBeanPostProcessors, // DisposableBean interface, custom destroy method. - registerDisposableBean(beanName, - new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc)); + registerDisposableBean(beanName, new DisposableBeanAdapter( + bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc)); } else { // A bean with a custom scope... @@ -1874,8 +1940,8 @@ protected void registerDisposableBeanIfNecessary(String beanName, Object bean, R if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'"); } - scope.registerDestructionCallback(beanName, - new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc)); + scope.registerDestructionCallback(beanName, new DisposableBeanAdapter( + bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc)); } } } @@ -1937,4 +2003,117 @@ protected void registerDisposableBeanIfNecessary(String beanName, Object bean, R protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException; + + /** + * CopyOnWriteArrayList which resets the beanPostProcessorCache field on modification. + * + * @since 5.3 + */ + private class BeanPostProcessorCacheAwareList extends CopyOnWriteArrayList { + + @Override + public BeanPostProcessor set(int index, BeanPostProcessor element) { + BeanPostProcessor result = super.set(index, element); + beanPostProcessorCache = null; + return result; + } + + @Override + public boolean add(BeanPostProcessor o) { + boolean success = super.add(o); + beanPostProcessorCache = null; + return success; + } + + @Override + public void add(int index, BeanPostProcessor element) { + super.add(index, element); + beanPostProcessorCache = null; + } + + @Override + public BeanPostProcessor remove(int index) { + BeanPostProcessor result = super.remove(index); + beanPostProcessorCache = null; + return result; + } + + @Override + public boolean remove(Object o) { + boolean success = super.remove(o); + if (success) { + beanPostProcessorCache = null; + } + return success; + } + + @Override + public boolean removeAll(Collection c) { + boolean success = super.removeAll(c); + if (success) { + beanPostProcessorCache = null; + } + return success; + } + + @Override + public boolean retainAll(Collection c) { + boolean success = super.retainAll(c); + if (success) { + beanPostProcessorCache = null; + } + return success; + } + + @Override + public boolean addAll(Collection c) { + boolean success = super.addAll(c); + if (success) { + beanPostProcessorCache = null; + } + return success; + } + + @Override + public boolean addAll(int index, Collection c) { + boolean success = super.addAll(index, c); + if (success) { + beanPostProcessorCache = null; + } + return success; + } + + @Override + public boolean removeIf(Predicate filter) { + boolean success = super.removeIf(filter); + if (success) { + beanPostProcessorCache = null; + } + return success; + } + + @Override + public void replaceAll(UnaryOperator operator) { + super.replaceAll(operator); + beanPostProcessorCache = null; + } + } + + + /** + * Internal cache of pre-filtered post-processors. + * + * @since 5.3 + */ + static class BeanPostProcessorCache { + + final List instantiationAware = new ArrayList<>(); + + final List smartInstantiationAware = new ArrayList<>(); + + final List destructionAware = new ArrayList<>(); + + final List mergedDefinition = new ArrayList<>(); + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java index bb1740e3972d..0a1f0a7d6910 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java @@ -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. @@ -16,6 +16,7 @@ package org.springframework.beans.factory.support; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.lang.Nullable; @@ -99,4 +100,22 @@ default Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor return null; } + /** + * Return a clone of this resolver instance if necessary, retaining its local + * configuration and allowing for the cloned instance to get associated with + * a new bean factory, or this original instance if there is no such state. + *

The default implementation creates a separate instance via the default + * class constructor, assuming no specific configuration state to copy. + * Subclasses may override this with custom configuration state handling + * or with standard {@link Cloneable} support (as implemented by Spring's + * own configurable {@code AutowireCandidateResolver} variants), or simply + * return {@code this} (as in {@link SimpleAutowireCandidateResolver}). + * @since 5.2.7 + * @see GenericTypeAwareAutowireCandidateResolver#cloneIfNecessary() + * @see DefaultListableBeanFactory#copyConfigurationFrom + */ + default AutowireCandidateResolver cloneIfNecessary() { + return BeanUtils.instantiateClass(getClass()); + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java index 67f952567573..d807cc4dc2fe 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java @@ -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. @@ -51,7 +51,7 @@ */ abstract class AutowireUtils { - private static final Comparator EXECUTABLE_COMPARATOR = (e1, e2) -> { + public static final Comparator EXECUTABLE_COMPARATOR = (e1, e2) -> { int result = Boolean.compare(Modifier.isPublic(e2.getModifiers()), Modifier.isPublic(e1.getModifiers())); return result != 0 ? result : Integer.compare(e2.getParameterCount(), e1.getParameterCount()); }; @@ -97,7 +97,7 @@ public static boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) { // It was declared by CGLIB, but we might still want to autowire it // if it was actually declared by the superclass. Class superclass = wm.getDeclaringClass().getSuperclass(); - return !ClassUtils.hasMethod(superclass, wm.getName(), wm.getParameterTypes()); + return !ClassUtils.hasMethod(superclass, wm); } /** @@ -112,8 +112,7 @@ public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set targetClass = setter.getDeclaringClass(); for (Class ifc : interfaces) { - if (ifc.isAssignableFrom(targetClass) && - ClassUtils.hasMethod(ifc, setter.getName(), setter.getParameterTypes())) { + if (ifc.isAssignableFrom(targetClass) && ClassUtils.hasMethod(ifc, setter)) { return true; } } @@ -273,23 +272,21 @@ private static class ObjectFactoryDelegatingInvocationHandler implements Invocat private final ObjectFactory objectFactory; - public ObjectFactoryDelegatingInvocationHandler(ObjectFactory objectFactory) { + ObjectFactoryDelegatingInvocationHandler(ObjectFactory objectFactory) { this.objectFactory = objectFactory; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - String methodName = method.getName(); - if (methodName.equals("equals")) { - // Only consider equal when proxies are identical. - return (proxy == args[0]); - } - else if (methodName.equals("hashCode")) { - // Use hashCode of proxy. - return System.identityHashCode(proxy); - } - else if (methodName.equals("toString")) { - return this.objectFactory.toString(); + switch (method.getName()) { + case "equals": + // Only consider equal when proxies are identical. + return (proxy == args[0]); + case "hashCode": + // Use hashCode of proxy. + return System.identityHashCode(proxy); + case "toString": + return this.objectFactory.toString(); } try { return method.invoke(this.objectFactory.getObject(), args); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionDefaults.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionDefaults.java index f98567a07070..5da53bfe3b67 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionDefaults.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionDefaults.java @@ -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. @@ -25,6 +25,7 @@ * @author Mark Fisher * @author Juergen Hoeller * @since 2.5 + * @see AbstractBeanDefinition#applyDefaults */ public class BeanDefinitionDefaults { @@ -46,6 +47,7 @@ public class BeanDefinitionDefaults { * Set whether beans should be lazily initialized by default. *

If {@code false}, the bean will get instantiated on startup by bean * factories that perform eager initialization of singletons. + * @see AbstractBeanDefinition#setLazyInit */ public void setLazyInit(boolean lazyInit) { this.lazyInit = lazyInit; @@ -78,6 +80,7 @@ public Boolean getLazyInit() { * (however, there may still be explicit annotation-driven autowiring). * @param autowireMode the autowire mode to set. * Must be one of the constants defined in {@link AbstractBeanDefinition}. + * @see AbstractBeanDefinition#setAutowireMode */ public void setAutowireMode(int autowireMode) { this.autowireMode = autowireMode; @@ -94,6 +97,7 @@ public int getAutowireMode() { * Set the dependency check code. * @param dependencyCheck the code to set. * Must be one of the constants defined in {@link AbstractBeanDefinition}. + * @see AbstractBeanDefinition#setDependencyCheck */ public void setDependencyCheck(int dependencyCheck) { this.dependencyCheck = dependencyCheck; @@ -108,6 +112,10 @@ public int getDependencyCheck() { /** * Set the name of the default initializer method. + *

Note that this method is not enforced on all affected bean definitions + * but rather taken as an optional callback, to be invoked if actually present. + * @see AbstractBeanDefinition#setInitMethodName + * @see AbstractBeanDefinition#setEnforceInitMethod */ public void setInitMethodName(@Nullable String initMethodName) { this.initMethodName = (StringUtils.hasText(initMethodName) ? initMethodName : null); @@ -123,6 +131,10 @@ public String getInitMethodName() { /** * Set the name of the default destroy method. + *

Note that this method is not enforced on all affected bean definitions + * but rather taken as an optional callback, to be invoked if actually present. + * @see AbstractBeanDefinition#setDestroyMethodName + * @see AbstractBeanDefinition#setEnforceDestroyMethod */ public void setDestroyMethodName(@Nullable String destroyMethodName) { this.destroyMethodName = (StringUtils.hasText(destroyMethodName) ? destroyMethodName : null); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java index f96c96fbdc3b..b02687792e15 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java @@ -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. @@ -118,16 +118,13 @@ else if (definition.getFactoryBeanName() != null) { "'class' nor 'parent' nor 'factory-bean' - can't generate bean name"); } - String id = generatedBeanName; if (isInnerBean) { // Inner bean: generate identity hashcode suffix. - id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition); + return generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition); } - else { - // Top-level bean: use plain class name with unique suffix if necessary. - return uniqueBeanName(generatedBeanName, registry); - } - return id; + + // Top-level bean: use plain class name with unique suffix if necessary. + return uniqueBeanName(generatedBeanName, registry); } /** @@ -144,9 +141,10 @@ public static String uniqueBeanName(String beanName, BeanDefinitionRegistry regi int counter = -1; // Increase counter until the id is unique. + String prefix = beanName + GENERATED_BEAN_NAME_SEPARATOR; while (counter == -1 || registry.containsBeanDefinition(id)) { counter++; - id = beanName + GENERATED_BEAN_NAME_SEPARATOR + counter; + id = prefix + counter; } return id; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java index abb5040f8992..be9667b19a38 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ import java.lang.reflect.Array; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -42,6 +41,7 @@ import org.springframework.beans.factory.config.TypedStringValue; import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; +import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -400,9 +400,10 @@ private Object resolveInnerBean(Object argName, String innerBeanName, BeanDefini private String adaptInnerBeanName(String innerBeanName) { String actualInnerBeanName = innerBeanName; int counter = 0; + String prefix = innerBeanName + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR; while (this.beanFactory.isBeanNameInUse(actualInnerBeanName)) { counter++; - actualInnerBeanName = innerBeanName + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + counter; + actualInnerBeanName = prefix + counter; } return actualInnerBeanName; } @@ -446,7 +447,7 @@ private Set resolveManagedSet(Object argName, Set ms) { * For each element in the managed map, resolve reference if necessary. */ private Map resolveManagedMap(Object argName, Map mm) { - Map resolved = new LinkedHashMap<>(mm.size()); + Map resolved = CollectionUtils.newLinkedHashMap(mm.size()); mm.forEach((key, value) -> { Object resolvedKey = resolveValueIfNecessary(argName, key); Object resolvedValue = resolveValueIfNecessary(new KeyedArgName(argName, key), value); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java index 88e9942e807b..698a04d2498a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java @@ -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. @@ -238,8 +238,10 @@ public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp Assert.state(lo != null, "LookupOverride not found"); Object[] argsToUse = (args.length > 0 ? args : null); // if no-arg, don't insist on args at all if (StringUtils.hasText(lo.getBeanName())) { - return (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) : + Object bean = (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) : this.owner.getBean(lo.getBeanName())); + // Detect package-protected NullBean instance through equals(null) check + return (bean.equals(null) ? null : bean); } else { return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) : diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index 8dcfff8d3ee8..7647f11defa4 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -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. @@ -24,12 +24,13 @@ import java.lang.reflect.Modifier; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Deque; import java.util.HashSet; import java.util.LinkedHashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -66,6 +67,7 @@ /** * Delegate for resolving constructors and factory methods. + * *

Performs constructor resolution through argument matching. * * @author Juergen Hoeller @@ -84,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(); @@ -148,7 +150,7 @@ public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, } } if (argsToResolve != null) { - argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true); + argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve); } } @@ -199,24 +201,25 @@ public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, AutowireUtils.sortConstructors(candidates); int minTypeDiffWeight = Integer.MAX_VALUE; Set> ambiguousConstructors = null; - LinkedList causes = null; + Deque causes = null; for (Constructor candidate : candidates) { - Class[] paramTypes = candidate.getParameterTypes(); + int parameterCount = candidate.getParameterCount(); - if (constructorToUse != null && argsToUse != null && argsToUse.length > paramTypes.length) { + if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) { // Already found greedy constructor that can be satisfied -> // do not look any further, there are only less greedy constructors left. break; } - if (paramTypes.length < minNrOfArgs) { + if (parameterCount < minNrOfArgs) { continue; } ArgumentsHolder argsHolder; + Class[] paramTypes = candidate.getParameterTypes(); if (resolvedValues != null) { try { - String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length); + String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount); if (paramNames == null) { ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer(); if (pnd != null) { @@ -232,7 +235,7 @@ public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, } // Swallow and try next constructor. if (causes == null) { - causes = new LinkedList<>(); + causes = new ArrayDeque<>(1); } causes.add(ex); continue; @@ -240,7 +243,7 @@ public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, } else { // Explicit arguments given -> arguments length must match exactly. - if (paramTypes.length != explicitArgs.length) { + if (parameterCount != explicitArgs.length) { continue; } argsHolder = new ArgumentsHolder(explicitArgs); @@ -340,7 +343,7 @@ public void resolveFactoryMethodIfPossible(RootBeanDefinition mbd) { if (uniqueCandidate == null) { uniqueCandidate = candidate; } - else if (!Arrays.equals(uniqueCandidate.getParameterTypes(), candidate.getParameterTypes())) { + else if (isParamMismatch(uniqueCandidate, candidate)) { uniqueCandidate = null; break; } @@ -349,6 +352,13 @@ else if (!Arrays.equals(uniqueCandidate.getParameterTypes(), candidate.getParame mbd.factoryMethodToIntrospect = uniqueCandidate; } + private boolean isParamMismatch(Method uniqueCandidate, Method candidate) { + int uniqueCandidateParameterCount = uniqueCandidate.getParameterCount(); + int candidateParameterCount = candidate.getParameterCount(); + return (uniqueCandidateParameterCount != candidateParameterCount || + !Arrays.equals(uniqueCandidate.getParameterTypes(), candidate.getParameterTypes())); + } + /** * Retrieve all candidate methods for the given class, considering * the {@link RootBeanDefinition#isNonPublicAccessAllowed()} flag. @@ -401,6 +411,7 @@ public BeanWrapper instantiateUsingFactoryMethod( if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) { throw new ImplicitlyAppearedSingletonException(); } + this.beanFactory.registerDependentBean(factoryBeanName, beanName); factoryClass = factoryBean.getClass(); isStatic = false; } @@ -435,7 +446,7 @@ public BeanWrapper instantiateUsingFactoryMethod( } } if (argsToResolve != null) { - argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true); + argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve); } } @@ -444,27 +455,27 @@ public BeanWrapper instantiateUsingFactoryMethod( // Try all methods with this name to see if they match the given arguments. factoryClass = ClassUtils.getUserClass(factoryClass); - List candidateList = null; + List candidates = null; if (mbd.isFactoryMethodUnique) { if (factoryMethodToUse == null) { factoryMethodToUse = mbd.getResolvedFactoryMethod(); } if (factoryMethodToUse != null) { - candidateList = Collections.singletonList(factoryMethodToUse); + candidates = Collections.singletonList(factoryMethodToUse); } } - if (candidateList == null) { - candidateList = new ArrayList<>(); + if (candidates == null) { + candidates = new ArrayList<>(); Method[] rawCandidates = getCandidateMethods(factoryClass, mbd); for (Method candidate : rawCandidates) { if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) { - candidateList.add(candidate); + candidates.add(candidate); } } } - if (candidateList.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) { - Method uniqueCandidate = candidateList.get(0); + if (candidates.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) { + Method uniqueCandidate = candidates.get(0); if (uniqueCandidate.getParameterCount() == 0) { mbd.factoryMethodToIntrospect = uniqueCandidate; synchronized (mbd.constructorArgumentLock) { @@ -477,8 +488,9 @@ public BeanWrapper instantiateUsingFactoryMethod( } } - Method[] candidates = candidateList.toArray(new Method[0]); - AutowireUtils.sortFactoryMethods(candidates); + if (candidates.size() > 1) { // explicitly skip immutable singletonList + candidates.sort(AutowireUtils.EXECUTABLE_COMPARATOR); + } ConstructorArgumentValues resolvedValues = null; boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR); @@ -502,14 +514,15 @@ public BeanWrapper instantiateUsingFactoryMethod( } } - LinkedList causes = null; + Deque causes = null; for (Method candidate : candidates) { - Class[] paramTypes = candidate.getParameterTypes(); + int parameterCount = candidate.getParameterCount(); - if (paramTypes.length >= minNrOfArgs) { + if (parameterCount >= minNrOfArgs) { ArgumentsHolder argsHolder; + Class[] paramTypes = candidate.getParameterTypes(); if (explicitArgs != null) { // Explicit arguments given -> arguments length must match exactly. if (paramTypes.length != explicitArgs.length) { @@ -526,7 +539,7 @@ public BeanWrapper instantiateUsingFactoryMethod( paramNames = pnd.getParameterNames(candidate); } argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, - paramTypes, paramNames, candidate, autowiring, candidates.length == 1); + paramTypes, paramNames, candidate, autowiring, candidates.size() == 1); } catch (UnsatisfiedDependencyException ex) { if (logger.isTraceEnabled()) { @@ -534,7 +547,7 @@ public BeanWrapper instantiateUsingFactoryMethod( } // Swallow and try next overloaded factory method. if (causes == null) { - causes = new LinkedList<>(); + causes = new ArrayDeque<>(1); } causes.add(ex); continue; @@ -668,7 +681,7 @@ private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid constructor argument index: " + index); } - if (index > minNrOfArgs) { + if (index + 1 > minNrOfArgs) { minNrOfArgs = index + 1; } ConstructorArgumentValues.ValueHolder valueHolder = entry.getValue(); @@ -805,7 +818,7 @@ private ArgumentsHolder createArgumentArray( * 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); @@ -818,7 +831,7 @@ private Object[] resolvePreparedArguments(String beanName, RootBeanDefinition mb 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); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 4f6dc67744bf..048612fbed73 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -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. @@ -33,7 +33,6 @@ import java.util.Comparator; import java.util.IdentityHashMap; import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -46,7 +45,6 @@ import javax.inject.Provider; -import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.TypeConverter; import org.springframework.beans.factory.BeanCreationException; @@ -68,7 +66,6 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; @@ -79,9 +76,11 @@ import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.log.LogMessage; +import org.springframework.core.metrics.StartupStep; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.CollectionUtils; import org.springframework.util.CompositeIterator; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -97,8 +96,7 @@ * operating on pre-resolved bean definition metadata objects. * *

Note that readers for specific bean definition formats are typically - * implemented separately rather than as bean factory subclasses: - * see for example {@link PropertiesBeanDefinitionReader} and + * implemented separately rather than as bean factory subclasses: see for example * {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}. * *

For an alternative implementation of the @@ -157,7 +155,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto private Comparator dependencyComparator; /** Resolver to use for checking if a bean definition is an autowire candidate. */ - private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver(); + private AutowireCandidateResolver autowireCandidateResolver = SimpleAutowireCandidateResolver.INSTANCE; /** Map from dependency type to corresponding autowired value. */ private final Map, Object> resolvableDependencies = new ConcurrentHashMap<>(16); @@ -165,6 +163,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto /** Map of bean definition objects, keyed by bean name. */ private final Map beanDefinitionMap = new ConcurrentHashMap<>(256); + /** Map from bean name to merged BeanDefinitionHolder. */ + private final Map mergedBeanDefinitionHolders = new ConcurrentHashMap<>(256); + /** Map of singleton and non-singleton bean names, keyed by dependency type. */ private final Map, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64); @@ -182,7 +183,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto private volatile String[] frozenBeanDefinitionNames; /** Whether bean definition metadata may be cached for all beans. */ - private volatile boolean configurationFrozen = false; + private volatile boolean configurationFrozen; /** @@ -292,12 +293,12 @@ public Comparator getDependencyComparator() { * when deciding whether a bean definition should be considered as a * candidate for autowiring. */ - public void setAutowireCandidateResolver(final AutowireCandidateResolver autowireCandidateResolver) { + public void setAutowireCandidateResolver(AutowireCandidateResolver autowireCandidateResolver) { Assert.notNull(autowireCandidateResolver, "AutowireCandidateResolver must not be null"); if (autowireCandidateResolver instanceof BeanFactoryAware) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction) () -> { - ((BeanFactoryAware) autowireCandidateResolver).setBeanFactory(DefaultListableBeanFactory.this); + ((BeanFactoryAware) autowireCandidateResolver).setBeanFactory(this); return null; }, getAccessControlContext()); } @@ -324,10 +325,9 @@ public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) { this.allowBeanDefinitionOverriding = otherListableFactory.allowBeanDefinitionOverriding; this.allowEagerClassLoading = otherListableFactory.allowEagerClassLoading; this.dependencyComparator = otherListableFactory.dependencyComparator; - // A clone of the AutowireCandidateResolver since it is potentially BeanFactoryAware... - setAutowireCandidateResolver( - BeanUtils.instantiateClass(otherListableFactory.getAutowireCandidateResolver().getClass())); - // Make resolvable dependencies (e.g. ResourceLoader) available here as well... + // A clone of the AutowireCandidateResolver since it is potentially BeanFactoryAware + setAutowireCandidateResolver(otherListableFactory.getAutowireCandidateResolver().cloneIfNecessary()); + // Make resolvable dependencies (e.g. ResourceLoader) available here as well this.resolvableDependencies.putAll(otherListableFactory.resolvableDependencies); } } @@ -354,14 +354,51 @@ public T getBean(Class requiredType, @Nullable Object... args) throws Bea } @Override - public ObjectProvider getBeanProvider(Class requiredType) throws BeansException { + public ObjectProvider getBeanProvider(Class requiredType) { Assert.notNull(requiredType, "Required type must not be null"); - return getBeanProvider(ResolvableType.forRawClass(requiredType)); + return getBeanProvider(ResolvableType.forRawClass(requiredType), true); } - @SuppressWarnings("unchecked") @Override public ObjectProvider getBeanProvider(ResolvableType requiredType) { + return getBeanProvider(requiredType, true); + } + + + //--------------------------------------------------------------------- + // Implementation of ListableBeanFactory interface + //--------------------------------------------------------------------- + + @Override + public boolean containsBeanDefinition(String beanName) { + Assert.notNull(beanName, "Bean name must not be null"); + return this.beanDefinitionMap.containsKey(beanName); + } + + @Override + public int getBeanDefinitionCount() { + return this.beanDefinitionMap.size(); + } + + @Override + public String[] getBeanDefinitionNames() { + String[] frozenNames = this.frozenBeanDefinitionNames; + if (frozenNames != null) { + return frozenNames.clone(); + } + else { + return StringUtils.toStringArray(this.beanDefinitionNames); + } + } + + @Override + public ObjectProvider getBeanProvider(Class requiredType, boolean allowEagerInit) { + Assert.notNull(requiredType, "Required type must not be null"); + return getBeanProvider(ResolvableType.forRawClass(requiredType), allowEagerInit); + } + + @Override + public ObjectProvider getBeanProvider(ResolvableType requiredType, boolean allowEagerInit) { return new BeanObjectProvider() { @Override public T getObject() throws BeansException { @@ -382,23 +419,64 @@ public T getObject(Object... args) throws BeansException { @Override @Nullable public T getIfAvailable() throws BeansException { - return resolveBean(requiredType, null, false); + try { + return resolveBean(requiredType, null, false); + } + catch (ScopeNotActiveException ex) { + // Ignore resolved bean in non-active scope + return null; + } + } + @Override + public void ifAvailable(Consumer dependencyConsumer) throws BeansException { + T dependency = getIfAvailable(); + if (dependency != null) { + try { + dependencyConsumer.accept(dependency); + } + catch (ScopeNotActiveException ex) { + // Ignore resolved bean in non-active scope, even on scoped proxy invocation + } + } } @Override @Nullable public T getIfUnique() throws BeansException { - return resolveBean(requiredType, null, true); + try { + return resolveBean(requiredType, null, true); + } + catch (ScopeNotActiveException ex) { + // Ignore resolved bean in non-active scope + return null; + } } @Override + public void ifUnique(Consumer dependencyConsumer) throws BeansException { + T dependency = getIfUnique(); + if (dependency != null) { + try { + dependencyConsumer.accept(dependency); + } + catch (ScopeNotActiveException ex) { + // Ignore resolved bean in non-active scope, even on scoped proxy invocation + } + } + } + @SuppressWarnings("unchecked") + @Override public Stream stream() { - return Arrays.stream(getBeanNamesForTypedStream(requiredType)) + return Arrays.stream(getBeanNamesForTypedStream(requiredType, allowEagerInit)) .map(name -> (T) getBean(name)) .filter(bean -> !(bean instanceof NullBean)); } + @SuppressWarnings("unchecked") @Override public Stream orderedStream() { - String[] beanNames = getBeanNamesForTypedStream(requiredType); - Map matchingBeans = new LinkedHashMap<>(beanNames.length); + String[] beanNames = getBeanNamesForTypedStream(requiredType, allowEagerInit); + if (beanNames.length == 0) { + return Stream.empty(); + } + Map matchingBeans = CollectionUtils.newLinkedHashMap(beanNames.length); for (String beanName : beanNames) { Object beanInstance = getBean(beanName); if (!(beanInstance instanceof NullBean)) { @@ -433,35 +511,8 @@ else if (parent != null) { return null; } - private String[] getBeanNamesForTypedStream(ResolvableType requiredType) { - return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType); - } - - - //--------------------------------------------------------------------- - // Implementation of ListableBeanFactory interface - //--------------------------------------------------------------------- - - @Override - public boolean containsBeanDefinition(String beanName) { - Assert.notNull(beanName, "Bean name must not be null"); - return this.beanDefinitionMap.containsKey(beanName); - } - - @Override - public int getBeanDefinitionCount() { - return this.beanDefinitionMap.size(); - } - - @Override - public String[] getBeanDefinitionNames() { - String[] frozenNames = this.frozenBeanDefinitionNames; - if (frozenNames != null) { - return frozenNames.clone(); - } - else { - return StringUtils.toStringArray(this.beanDefinitionNames); - } + private String[] getBeanNamesForTypedStream(ResolvableType requiredType, boolean allowEagerInit) { + return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, allowEagerInit); } @Override @@ -473,10 +524,10 @@ public String[] getBeanNamesForType(ResolvableType type) { public String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) { Class resolved = type.resolve(); if (resolved != null && !type.hasGenerics()) { - return getBeanNamesForType(resolved, includeNonSingletons, includeNonSingletons); + return getBeanNamesForType(resolved, includeNonSingletons, allowEagerInit); } else { - return doGetBeanNamesForType(type, includeNonSingletons, includeNonSingletons); + return doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit); } } @@ -508,8 +559,7 @@ private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSi // Check all bean definitions. for (String beanName : this.beanDefinitionNames) { - // Only consider bean as eligible if the bean name - // is not defined as alias for some other bean. + // Only consider bean as eligible if the bean name is not defined as alias for some other bean. if (!isAlias(beanName)) { try { RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); @@ -520,14 +570,14 @@ private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSi boolean isFactoryBean = isFactoryBean(beanName, mbd); BeanDefinitionHolder dbd = mbd.getDecoratedDefinition(); boolean matchFound = false; - boolean allowFactoryBeanInit = allowEagerInit || containsSingleton(beanName); - boolean isNonLazyDecorated = dbd != null && !mbd.isLazyInit(); + boolean allowFactoryBeanInit = (allowEagerInit || containsSingleton(beanName)); + boolean isNonLazyDecorated = (dbd != null && !mbd.isLazyInit()); if (!isFactoryBean) { if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) { matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit); } } - else { + else { if (includeNonSingletons || isNonLazyDecorated || (allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) { matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit); @@ -548,16 +598,19 @@ private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSi throw ex; } // Probably a placeholder: let's ignore it for type matching purposes. - LogMessage message = (ex instanceof CannotLoadBeanClassException) ? + LogMessage message = (ex instanceof CannotLoadBeanClassException ? LogMessage.format("Ignoring bean class loading failure for bean '%s'", beanName) : - LogMessage.format("Ignoring unresolvable metadata in bean definition '%s'", beanName); + LogMessage.format("Ignoring unresolvable metadata in bean definition '%s'", beanName)); logger.trace(message, ex); + // Register exception, in case the bean was accidentally unresolvable. onSuppressedException(ex); } + catch (NoSuchBeanDefinitionException ex) { + // Bean definition got removed while we were iterating -> ignore. + } } } - // Check manually registered singletons too. for (String beanName : this.manualSingletonNames) { try { @@ -578,7 +631,8 @@ private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSi } catch (NoSuchBeanDefinitionException ex) { // Shouldn't happen - probably a result of circular reference resolution... - logger.trace(LogMessage.format("Failed to check manually registered singleton with name '%s'", beanName), ex); + logger.trace(LogMessage.format( + "Failed to check manually registered singleton with name '%s'", beanName), ex); } } @@ -607,11 +661,11 @@ public Map getBeansOfType(@Nullable Class type) throws BeansEx @Override @SuppressWarnings("unchecked") - public Map getBeansOfType(@Nullable Class type, boolean includeNonSingletons, boolean allowEagerInit) - throws BeansException { + public Map getBeansOfType( + @Nullable Class type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException { String[] beanNames = getBeanNamesForType(type, includeNonSingletons, allowEagerInit); - Map result = new LinkedHashMap<>(beanNames.length); + Map result = CollectionUtils.newLinkedHashMap(beanNames.length); for (String beanName : beanNames) { try { Object beanInstance = getBean(beanName); @@ -645,8 +699,8 @@ public Map getBeansOfType(@Nullable Class type, boolean includ public String[] getBeanNamesForAnnotation(Class annotationType) { List result = new ArrayList<>(); for (String beanName : this.beanDefinitionNames) { - BeanDefinition beanDefinition = getBeanDefinition(beanName); - if (!beanDefinition.isAbstract() && findAnnotationOnBean(beanName, annotationType) != null) { + BeanDefinition bd = this.beanDefinitionMap.get(beanName); + if (bd != null && !bd.isAbstract() && findAnnotationOnBean(beanName, annotationType) != null) { result.add(beanName); } } @@ -661,7 +715,7 @@ public String[] getBeanNamesForAnnotation(Class annotation @Override public Map getBeansWithAnnotation(Class annotationType) { String[] beanNames = getBeanNamesForAnnotation(annotationType); - Map result = new LinkedHashMap<>(beanNames.length); + Map result = CollectionUtils.newLinkedHashMap(beanNames.length); for (String beanName : beanNames) { Object beanInstance = getBean(beanName); if (!(beanInstance instanceof NullBean)) { @@ -749,12 +803,13 @@ public boolean isAutowireCandidate(String beanName, DependencyDescriptor descrip * @param resolver the AutowireCandidateResolver to use for the actual resolution algorithm * @return whether the bean should be considered as autowire candidate */ - protected boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor, AutowireCandidateResolver resolver) + protected boolean isAutowireCandidate( + String beanName, DependencyDescriptor descriptor, AutowireCandidateResolver resolver) throws NoSuchBeanDefinitionException { - String beanDefinitionName = BeanFactoryUtils.transformedBeanName(beanName); - if (containsBeanDefinition(beanDefinitionName)) { - return isAutowireCandidate(beanName, getMergedLocalBeanDefinition(beanDefinitionName), descriptor, resolver); + String bdName = BeanFactoryUtils.transformedBeanName(beanName); + if (containsBeanDefinition(bdName)) { + return isAutowireCandidate(beanName, getMergedLocalBeanDefinition(bdName), descriptor, resolver); } else if (containsSingleton(beanName)) { return isAutowireCandidate(beanName, new RootBeanDefinition(getType(beanName)), descriptor, resolver); @@ -786,13 +841,16 @@ else if (parent instanceof ConfigurableListableBeanFactory) { protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd, DependencyDescriptor descriptor, AutowireCandidateResolver resolver) { - String beanDefinitionName = BeanFactoryUtils.transformedBeanName(beanName); - resolveBeanClass(mbd, beanDefinitionName); + String bdName = BeanFactoryUtils.transformedBeanName(beanName); + resolveBeanClass(mbd, bdName); if (mbd.isFactoryMethodUnique && mbd.factoryMethodToIntrospect == null) { new ConstructorResolver(this).resolveFactoryMethodIfPossible(mbd); } - return resolver.isAutowireCandidate( - new BeanDefinitionHolder(mbd, beanName, getAliases(beanDefinitionName)), descriptor); + BeanDefinitionHolder holder = (beanName.equals(bdName) ? + this.mergedBeanDefinitionHolders.computeIfAbsent(beanName, + key -> new BeanDefinitionHolder(mbd, beanName, getAliases(bdName))) : + new BeanDefinitionHolder(mbd, beanName, getAliases(bdName))); + return resolver.isAutowireCandidate(holder, descriptor); } @Override @@ -815,9 +873,16 @@ public Iterator getBeanNamesIterator() { return iterator; } + @Override + protected void clearMergedBeanDefinition(String beanName) { + super.clearMergedBeanDefinition(beanName); + this.mergedBeanDefinitionHolders.remove(beanName); + } + @Override public void clearMetadataCache() { super.clearMetadataCache(); + this.mergedBeanDefinitionHolders.clear(); clearByTypeCache(); } @@ -859,11 +924,11 @@ public void preInstantiateSingletons() throws BeansException { if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { - final FactoryBean factory = (FactoryBean) bean; + FactoryBean factory = (FactoryBean) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { - isEagerInit = AccessController.doPrivileged((PrivilegedAction) - ((SmartFactoryBean) factory)::isEagerInit, + isEagerInit = AccessController.doPrivileged( + (PrivilegedAction) ((SmartFactoryBean) factory)::isEagerInit, getAccessControlContext()); } else { @@ -885,7 +950,9 @@ public void preInstantiateSingletons() throws BeansException { for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { - final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; + StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize") + .tag("beanName", beanName); + SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction) () -> { smartSingleton.afterSingletonsInstantiated(); @@ -895,6 +962,7 @@ public void preInstantiateSingletons() throws BeansException { else { smartSingleton.afterSingletonsInstantiated(); } + smartInitialize.end(); } } } @@ -974,6 +1042,9 @@ else if (!beanDefinition.equals(existingDefinition)) { if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } + else if (isConfigurationFrozen()) { + clearByTypeCache(); + } } @Override @@ -1026,18 +1097,15 @@ protected void resetBeanDefinition(String beanName) { destroySingleton(beanName); // Notify all post-processors that the specified bean definition has been reset. - for (BeanPostProcessor processor : getBeanPostProcessors()) { - if (processor instanceof MergedBeanDefinitionPostProcessor) { - ((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName); - } + for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) { + processor.resetBeanDefinition(beanName); } // Reset all bean definitions that have the given bean as parent (recursively). for (String bdName : this.beanDefinitionNames) { if (!beanName.equals(bdName)) { BeanDefinition bd = this.beanDefinitionMap.get(bdName); - // Ensure bd is non-null due to potential concurrent modification - // of the beanDefinitionMap. + // Ensure bd is non-null due to potential concurrent modification of beanDefinitionMap. if (bd != null && beanName.equals(bd.getParentName())) { resetBeanDefinition(bdName); } @@ -1053,6 +1121,18 @@ protected boolean allowAliasOverriding() { return isAllowBeanDefinitionOverriding(); } + /** + * Also checks for an alias overriding a bean definition of the same name. + */ + @Override + protected void checkForAliasCircle(String name, String alias) { + super.checkForAliasCircle(name, alias); + if (!isAllowBeanDefinitionOverriding() && containsBeanDefinition(alias)) { + throw new IllegalStateException("Cannot register alias '" + alias + + "' for name '" + name + "': Alias would override bean definition '" + alias + "'"); + } + } + @Override public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException { super.registerSingleton(beanName, singletonObject); @@ -1151,11 +1231,10 @@ private NamedBeanHolder resolveNamedBean( } if (candidateNames.length == 1) { - String beanName = candidateNames[0]; - return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args)); + return resolveNamedBean(candidateNames[0], requiredType, args); } else if (candidateNames.length > 1) { - Map candidates = new LinkedHashMap<>(candidateNames.length); + Map candidates = CollectionUtils.newLinkedHashMap(candidateNames.length); for (String beanName : candidateNames) { if (containsSingleton(beanName) && args == null) { Object beanInstance = getBean(beanName); @@ -1171,8 +1250,11 @@ else if (candidateNames.length > 1) { } if (candidateName != null) { Object beanInstance = candidates.get(candidateName); - if (beanInstance == null || beanInstance instanceof Class) { - beanInstance = getBean(candidateName, requiredType.toClass(), args); + if (beanInstance == null) { + return null; + } + if (beanInstance instanceof Class) { + return resolveNamedBean(candidateName, requiredType, args); } return new NamedBeanHolder<>(candidateName, (T) beanInstance); } @@ -1184,6 +1266,17 @@ else if (candidateNames.length > 1) { return null; } + @Nullable + private NamedBeanHolder resolveNamedBean( + String beanName, ResolvableType requiredType, @Nullable Object[] args) throws BeansException { + + Object bean = getBean(beanName, null, args); + if (bean instanceof NullBean) { + return null; + } + return new NamedBeanHolder(beanName, adaptBeanInstance(beanName, bean, requiredType.toClass())); + } + @Override @Nullable public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @@ -1307,7 +1400,7 @@ public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable Str private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) { - final Class type = descriptor.getDependencyType(); + Class type = descriptor.getDependencyType(); if (descriptor instanceof StreamDependencyDescriptor) { Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); @@ -1366,9 +1459,11 @@ else if (Collection.class.isAssignableFrom(type) && type.isInterface()) { TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); Object result = converter.convertIfNecessary(matchingBeans.values(), type); if (result instanceof List) { - Comparator comparator = adaptDependencyComparator(matchingBeans); - if (comparator != null) { - ((List) result).sort(comparator); + if (((List) result).size() > 1) { + Comparator comparator = adaptDependencyComparator(matchingBeans); + if (comparator != null) { + ((List) result).sort(comparator); + } } } return result; @@ -1450,7 +1545,7 @@ protected Map findAutowireCandidates( String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); - Map result = new LinkedHashMap<>(candidateNames.length); + Map result = CollectionUtils.newLinkedHashMap(candidateNames.length); for (Map.Entry, Object> classObjectEntry : this.resolvableDependencies.entrySet()) { Class autowiringType = classObjectEntry.getKey(); if (autowiringType.isAssignableFrom(requiredType)) { @@ -1701,18 +1796,23 @@ private void raiseNoMatchingBeanFound( */ private void checkBeanNotOfRequiredType(Class type, DependencyDescriptor descriptor) { for (String beanName : this.beanDefinitionNames) { - RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); - Class targetType = mbd.getTargetType(); - if (targetType != null && type.isAssignableFrom(targetType) && - isAutowireCandidate(beanName, mbd, descriptor, getAutowireCandidateResolver())) { - // Probably a proxy interfering with target type match -> throw meaningful exception. - Object beanInstance = getSingleton(beanName, false); - Class beanType = (beanInstance != null && beanInstance.getClass() != NullBean.class ? - beanInstance.getClass() : predictBeanType(beanName, mbd)); - if (beanType != null && !type.isAssignableFrom(beanType)) { - throw new BeanNotOfRequiredTypeException(beanName, type, beanType); + try { + RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); + Class targetType = mbd.getTargetType(); + if (targetType != null && type.isAssignableFrom(targetType) && + isAutowireCandidate(beanName, mbd, descriptor, getAutowireCandidateResolver())) { + // Probably a proxy interfering with target type match -> throw meaningful exception. + Object beanInstance = getSingleton(beanName, false); + Class beanType = (beanInstance != null && beanInstance.getClass() != NullBean.class ? + beanInstance.getClass() : predictBeanType(beanName, mbd)); + if (beanType != null && !type.isAssignableFrom(beanType)) { + throw new BeanNotOfRequiredTypeException(beanName, type, beanType); + } } } + catch (NoSuchBeanDefinitionException ex) { + // Bean definition got removed while we were iterating -> ignore. + } } BeanFactory parent = getParentBeanFactory(); @@ -1907,17 +2007,36 @@ public Object resolveCandidate(String beanName, Class requiredType, BeanFacto @Override @Nullable public Object getIfAvailable() throws BeansException { - if (this.optional) { - return createOptionalDependency(this.descriptor, this.beanName); + try { + if (this.optional) { + return createOptionalDependency(this.descriptor, this.beanName); + } + else { + DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) { + @Override + public boolean isRequired() { + return false; + } + }; + return doResolveDependency(descriptorToUse, this.beanName, null, null); + } } - else { - DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) { - @Override - public boolean isRequired() { - return false; - } - }; - return doResolveDependency(descriptorToUse, this.beanName, null, null); + catch (ScopeNotActiveException ex) { + // Ignore resolved bean in non-active scope + return null; + } + } + + @Override + public void ifAvailable(Consumer dependencyConsumer) throws BeansException { + Object dependency = getIfAvailable(); + if (dependency != null) { + try { + dependencyConsumer.accept(dependency); + } + catch (ScopeNotActiveException ex) { + // Ignore resolved bean in non-active scope, even on scoped proxy invocation + } } } @@ -1929,17 +2048,37 @@ public Object getIfUnique() throws BeansException { public boolean isRequired() { return false; } + @Override @Nullable public Object resolveNotUnique(ResolvableType type, Map matchingBeans) { return null; } }; - if (this.optional) { - return createOptionalDependency(descriptorToUse, this.beanName); + try { + if (this.optional) { + return createOptionalDependency(descriptorToUse, this.beanName); + } + else { + return doResolveDependency(descriptorToUse, this.beanName, null, null); + } } - else { - return doResolveDependency(descriptorToUse, this.beanName, null, null); + catch (ScopeNotActiveException ex) { + // Ignore resolved bean in non-active scope + return null; + } + } + + @Override + public void ifUnique(Consumer dependencyConsumer) throws BeansException { + Object dependency = getIfUnique(); + if (dependency != null) { + try { + dependencyConsumer.accept(dependency); + } + catch (ScopeNotActiveException ex) { + // Ignore resolved bean in non-active scope, even on scoped proxy invocation + } } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java index 737566691260..0a81aef4e9fe 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java @@ -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. @@ -70,6 +70,10 @@ */ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { + /** Maximum number of suppressed exceptions to preserve. */ + private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100; + + /** Cache of singleton objects: bean name to bean instance. */ private final Map singletonObjects = new ConcurrentHashMap<>(256); @@ -77,7 +81,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements private final Map> singletonFactories = new HashMap<>(16); /** Cache of early singleton objects: bean name to bean instance. */ - private final Map earlySingletonObjects = new HashMap<>(16); + private final Map earlySingletonObjects = new ConcurrentHashMap<>(16); /** Set of registered singletons, containing the bean names in registration order. */ private final Set registeredSingletons = new LinkedHashSet<>(256); @@ -90,7 +94,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements private final Set inCreationCheckExclusions = Collections.newSetFromMap(new ConcurrentHashMap<>(16)); - /** List of suppressed Exceptions, available for associating related causes. */ + /** Collection of suppressed Exceptions, available for associating related causes. */ @Nullable private Set suppressedExceptions; @@ -174,16 +178,24 @@ public Object getSingleton(String beanName) { */ @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { + // Quick check for existing instance without full singleton lock Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { - synchronized (this.singletonObjects) { - singletonObject = this.earlySingletonObjects.get(beanName); - if (singletonObject == null && allowEarlyReference) { - ObjectFactory singletonFactory = this.singletonFactories.get(beanName); - if (singletonFactory != null) { - singletonObject = singletonFactory.getObject(); - this.earlySingletonObjects.put(beanName, singletonObject); - this.singletonFactories.remove(beanName); + singletonObject = this.earlySingletonObjects.get(beanName); + if (singletonObject == null && allowEarlyReference) { + synchronized (this.singletonObjects) { + // Consistent creation of early reference within full singleton lock + singletonObject = this.singletonObjects.get(beanName); + if (singletonObject == null) { + singletonObject = this.earlySingletonObjects.get(beanName); + if (singletonObject == null) { + ObjectFactory singletonFactory = this.singletonFactories.get(beanName); + if (singletonFactory != null) { + singletonObject = singletonFactory.getObject(); + this.earlySingletonObjects.put(beanName, singletonObject); + this.singletonFactories.remove(beanName); + } + } } } } @@ -253,13 +265,17 @@ public Object getSingleton(String beanName, ObjectFactory singletonFactory) { } /** - * Register an Exception that happened to get suppressed during the creation of a + * Register an exception that happened to get suppressed during the creation of a * singleton bean instance, e.g. a temporary circular reference resolution problem. + *

The default implementation preserves any given exception in this registry's + * collection of suppressed exceptions, up to a limit of 100 exceptions, adding + * them as related causes to an eventual top-level {@link BeanCreationException}. * @param ex the Exception to register + * @see BeanCreationException#getRelatedCauses() */ protected void onSuppressedException(Exception ex) { synchronized (this.singletonObjects) { - if (this.suppressedExceptions != null) { + if (this.suppressedExceptions != null && this.suppressedExceptions.size() < SUPPRESSED_EXCEPTIONS_LIMIT) { this.suppressedExceptions.add(ex); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java index 36768418cdd8..3e779752a952 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,6 @@ import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -87,7 +86,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { private transient Method destroyMethod; @Nullable - private List beanPostProcessors; + private final List beanPostProcessors; /** @@ -99,7 +98,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { * (potentially DestructionAwareBeanPostProcessor), if any */ public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition, - List postProcessors, @Nullable AccessControlContext acc) { + List postProcessors, @Nullable AccessControlContext acc) { Assert.notNull(bean, "Disposable bean must not be null"); this.bean = bean; @@ -120,14 +119,16 @@ public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition be } } else { - Class[] paramTypes = destroyMethod.getParameterTypes(); - if (paramTypes.length > 1) { - throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" + - beanName + "' has more than one parameter - not supported as destroy method"); - } - else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) { - throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" + - beanName + "' has a non-boolean parameter - not supported as destroy method"); + if (destroyMethod.getParameterCount() > 0) { + Class[] paramTypes = destroyMethod.getParameterTypes(); + if (paramTypes.length > 1) { + throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" + + beanName + "' has more than one parameter - not supported as destroy method"); + } + else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) { + throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" + + beanName + "' has a non-boolean parameter - not supported as destroy method"); + } } destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod); } @@ -142,7 +143,9 @@ else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) { * @param postProcessors the List of BeanPostProcessors * (potentially DestructionAwareBeanPostProcessor), if any */ - public DisposableBeanAdapter(Object bean, List postProcessors, AccessControlContext acc) { + public DisposableBeanAdapter( + Object bean, List postProcessors, AccessControlContext acc) { + Assert.notNull(bean, "Disposable bean must not be null"); this.bean = bean; this.beanName = bean.getClass().getName(); @@ -169,67 +172,6 @@ private DisposableBeanAdapter(Object bean, String beanName, boolean invokeDispos } - /** - * If the current value of the given beanDefinition's "destroyMethodName" property is - * {@link AbstractBeanDefinition#INFER_METHOD}, then attempt to infer a destroy method. - * Candidate methods are currently limited to public, no-arg methods named "close" or - * "shutdown" (whether declared locally or inherited). The given BeanDefinition's - * "destroyMethodName" is updated to be null if no such method is found, otherwise set - * to the name of the inferred method. This constant serves as the default for the - * {@code @Bean#destroyMethod} attribute and the value of the constant may also be - * used in XML within the {@code } or {@code - * } attributes. - *

Also processes the {@link java.io.Closeable} and {@link java.lang.AutoCloseable} - * interfaces, reflectively calling the "close" method on implementing beans as well. - */ - @Nullable - private String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) { - String destroyMethodName = beanDefinition.getDestroyMethodName(); - if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) || - (destroyMethodName == null && bean instanceof AutoCloseable)) { - // Only perform destroy method inference or Closeable detection - // in case of the bean not explicitly implementing DisposableBean - if (!(bean instanceof DisposableBean)) { - try { - return bean.getClass().getMethod(CLOSE_METHOD_NAME).getName(); - } - catch (NoSuchMethodException ex) { - try { - return bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName(); - } - catch (NoSuchMethodException ex2) { - // no candidate destroy method found - } - } - } - return null; - } - return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null); - } - - /** - * Search for all DestructionAwareBeanPostProcessors in the List. - * @param processors the List to search - * @return the filtered List of DestructionAwareBeanPostProcessors - */ - @Nullable - private List filterPostProcessors(List processors, Object bean) { - List filteredPostProcessors = null; - if (!CollectionUtils.isEmpty(processors)) { - filteredPostProcessors = new ArrayList<>(processors.size()); - for (BeanPostProcessor processor : processors) { - if (processor instanceof DestructionAwareBeanPostProcessor) { - DestructionAwareBeanPostProcessor dabpp = (DestructionAwareBeanPostProcessor) processor; - if (dabpp.requiresDestruction(bean)) { - filteredPostProcessors.add(dabpp); - } - } - } - } - return filteredPostProcessors; - } - - @Override public void run() { destroy(); @@ -311,9 +253,9 @@ private Method findDestroyMethod(String name) { * assuming a "force" parameter), else logging an error. */ private void invokeCustomDestroyMethod(final Method destroyMethod) { - Class[] paramTypes = destroyMethod.getParameterTypes(); - final Object[] args = new Object[paramTypes.length]; - if (paramTypes.length == 1) { + int paramCount = destroyMethod.getParameterCount(); + final Object[] args = new Object[paramCount]; + if (paramCount == 1) { args[0] = Boolean.TRUE; } if (logger.isTraceEnabled()) { @@ -384,12 +326,50 @@ public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefin if (bean instanceof DisposableBean || bean instanceof AutoCloseable) { return true; } - String destroyMethodName = beanDefinition.getDestroyMethodName(); - if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName)) { - return (ClassUtils.hasMethod(bean.getClass(), CLOSE_METHOD_NAME) || - ClassUtils.hasMethod(bean.getClass(), SHUTDOWN_METHOD_NAME)); + return inferDestroyMethodIfNecessary(bean, beanDefinition) != null; + } + + + /** + * If the current value of the given beanDefinition's "destroyMethodName" property is + * {@link AbstractBeanDefinition#INFER_METHOD}, then attempt to infer a destroy method. + * Candidate methods are currently limited to public, no-arg methods named "close" or + * "shutdown" (whether declared locally or inherited). The given BeanDefinition's + * "destroyMethodName" is updated to be null if no such method is found, otherwise set + * to the name of the inferred method. This constant serves as the default for the + * {@code @Bean#destroyMethod} attribute and the value of the constant may also be + * used in XML within the {@code } or {@code + * } attributes. + *

Also processes the {@link java.io.Closeable} and {@link java.lang.AutoCloseable} + * interfaces, reflectively calling the "close" method on implementing beans as well. + */ + @Nullable + private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) { + String destroyMethodName = beanDefinition.resolvedDestroyMethodName; + if (destroyMethodName == null) { + destroyMethodName = beanDefinition.getDestroyMethodName(); + if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) || + (destroyMethodName == null && bean instanceof AutoCloseable)) { + // Only perform destroy method inference or Closeable detection + // in case of the bean not explicitly implementing DisposableBean + destroyMethodName = null; + if (!(bean instanceof DisposableBean)) { + try { + destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName(); + } + catch (NoSuchMethodException ex) { + try { + destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName(); + } + catch (NoSuchMethodException ex2) { + // no candidate destroy method found + } + } + } + } + beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : ""); } - return StringUtils.hasLength(destroyMethodName); + return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null); } /** @@ -397,18 +377,36 @@ public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefin * @param bean the bean instance * @param postProcessors the post-processor candidates */ - public static boolean hasApplicableProcessors(Object bean, List postProcessors) { + public static boolean hasApplicableProcessors(Object bean, List postProcessors) { if (!CollectionUtils.isEmpty(postProcessors)) { - for (BeanPostProcessor processor : postProcessors) { - if (processor instanceof DestructionAwareBeanPostProcessor) { - DestructionAwareBeanPostProcessor dabpp = (DestructionAwareBeanPostProcessor) processor; - if (dabpp.requiresDestruction(bean)) { - return true; - } + for (DestructionAwareBeanPostProcessor processor : postProcessors) { + if (processor.requiresDestruction(bean)) { + return true; } } } return false; } + /** + * Search for all DestructionAwareBeanPostProcessors in the List. + * @param processors the List to search + * @return the filtered List of DestructionAwareBeanPostProcessors + */ + @Nullable + private static List filterPostProcessors( + List processors, Object bean) { + + List filteredPostProcessors = null; + if (!CollectionUtils.isEmpty(processors)) { + filteredPostProcessors = new ArrayList<>(processors.size()); + for (DestructionAwareBeanPostProcessor processor : processors) { + if (processor.requiresDestruction(bean)) { + filteredPostProcessors.add(processor); + } + } + } + return filteredPostProcessors; + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java index ce865d11305f..fc689a7aec32 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java @@ -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,11 +54,11 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg * or {@code null} if the type cannot be determined yet */ @Nullable - protected Class getTypeForFactoryBean(final FactoryBean factoryBean) { + protected Class getTypeForFactoryBean(FactoryBean factoryBean) { try { if (System.getSecurityManager() != null) { - return AccessController.doPrivileged((PrivilegedAction>) - factoryBean::getObjectType, getAccessControlContext()); + return AccessController.doPrivileged( + (PrivilegedAction>) factoryBean::getObjectType, getAccessControlContext()); } else { return factoryBean.getObjectType(); @@ -153,9 +153,7 @@ protected Object getObjectFromFactoryBean(FactoryBean factory, String beanNam * @throws BeanCreationException if FactoryBean object creation failed * @see org.springframework.beans.factory.FactoryBean#getObject() */ - private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName) - throws BeanCreationException { - + private Object doGetObjectFromFactoryBean(FactoryBean factory, String beanName) throws BeanCreationException { Object object; try { if (System.getSecurityManager() != null) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java index 7143fc9b1fa1..01b81450903d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java @@ -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. @@ -43,7 +43,7 @@ * @since 4.0 */ public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCandidateResolver - implements BeanFactoryAware { + implements BeanFactoryAware, Cloneable { @Nullable private BeanFactory beanFactory; @@ -177,4 +177,21 @@ protected ResolvableType getReturnTypeForFactoryMethod(RootBeanDefinition rbd, D return null; } + + /** + * This implementation clones all instance fields through standard + * {@link Cloneable} support, allowing for subsequent reconfiguration + * of the cloned instance through a fresh {@link #setBeanFactory} call. + * @see #clone() + */ + @Override + public AutowireCandidateResolver cloneIfNecessary() { + try { + return (AutowireCandidateResolver) clone(); + } + catch (CloneNotSupportedException ex) { + throw new IllegalStateException(ex); + } + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/PropertiesBeanDefinitionReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/PropertiesBeanDefinitionReader.java index 2e620dc11a00..617a13be564b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/PropertiesBeanDefinitionReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/PropertiesBeanDefinitionReader.java @@ -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. @@ -30,12 +30,13 @@ import org.springframework.beans.PropertyAccessor; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.CannotLoadBeanClassException; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.core.io.Resource; import org.springframework.core.io.support.EncodedResource; +import org.springframework.core.io.support.ResourcePropertiesPersister; import org.springframework.lang.Nullable; -import org.springframework.util.DefaultPropertiesPersister; import org.springframework.util.PropertiesPersister; import org.springframework.util.StringUtils; @@ -73,7 +74,10 @@ * @author Rob Harrop * @since 26.11.2003 * @see DefaultListableBeanFactory + * @deprecated as of 5.3, in favor of Spring's common bean definition formats + * and/or custom reader implementations */ +@Deprecated public class PropertiesBeanDefinitionReader extends AbstractBeanDefinitionReader { /** @@ -144,7 +148,7 @@ public class PropertiesBeanDefinitionReader extends AbstractBeanDefinitionReader @Nullable private String defaultParentBean; - private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister(); + private PropertiesPersister propertiesPersister = ResourcePropertiesPersister.INSTANCE; /** @@ -183,12 +187,12 @@ public String getDefaultParentBean() { /** * Set the PropertiesPersister to use for parsing properties files. - * The default is DefaultPropertiesPersister. - * @see org.springframework.util.DefaultPropertiesPersister + * The default is ResourcePropertiesPersister. + * @see ResourcePropertiesPersister#INSTANCE */ public void setPropertiesPersister(@Nullable PropertiesPersister propertiesPersister) { this.propertiesPersister = - (propertiesPersister != null ? propertiesPersister : new DefaultPropertiesPersister()); + (propertiesPersister != null ? propertiesPersister : ResourcePropertiesPersister.INSTANCE); } /** @@ -367,7 +371,7 @@ public int registerBeanDefinitions(Map map, @Nullable String prefix, Strin // Key is of form: prefix.property String nameAndProperty = keyString.substring(prefix.length()); // Find dot before property name, ignoring dots in property keys. - int sepIdx = -1; + int sepIdx ; int propKeyIdx = nameAndProperty.indexOf(PropertyAccessor.PROPERTY_KEY_PREFIX); if (propKeyIdx != -1) { sepIdx = nameAndProperty.lastIndexOf(SEPARATOR, propKeyIdx); @@ -414,17 +418,20 @@ protected void registerBeanDefinition(String beanName, Map map, String pre String className = null; String parent = null; - String scope = GenericBeanDefinition.SCOPE_SINGLETON; + String scope = BeanDefinition.SCOPE_SINGLETON; boolean isAbstract = false; boolean lazyInit = false; ConstructorArgumentValues cas = new ConstructorArgumentValues(); MutablePropertyValues pvs = new MutablePropertyValues(); + String prefixWithSep = prefix + SEPARATOR; + int beginIndex = prefixWithSep.length(); + for (Map.Entry entry : map.entrySet()) { String key = StringUtils.trimWhitespace((String) entry.getKey()); - if (key.startsWith(prefix + SEPARATOR)) { - String property = key.substring(prefix.length() + SEPARATOR.length()); + if (key.startsWith(prefixWithSep)) { + String property = key.substring(beginIndex); if (CLASS_KEY.equals(property)) { className = StringUtils.trimWhitespace((String) entry.getValue()); } @@ -442,8 +449,8 @@ else if (SCOPE_KEY.equals(property)) { else if (SINGLETON_KEY.equals(property)) { // Spring 1.2 style String val = StringUtils.trimWhitespace((String) entry.getValue()); - scope = ("".equals(val) || TRUE_VALUE.equals(val) ? GenericBeanDefinition.SCOPE_SINGLETON : - GenericBeanDefinition.SCOPE_PROTOTYPE); + scope = (!StringUtils.hasLength(val) || TRUE_VALUE.equals(val) ? + BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE); } else if (LAZY_INIT_KEY.equals(property)) { String val = StringUtils.trimWhitespace((String) entry.getValue()); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ReplaceOverride.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ReplaceOverride.java index c94ce6717628..1b51bf706553 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ReplaceOverride.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ReplaceOverride.java @@ -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,7 +17,7 @@ package org.springframework.beans.factory.support; import java.lang.reflect.Method; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import org.springframework.lang.Nullable; @@ -39,7 +39,7 @@ public class ReplaceOverride extends MethodOverride { private final String methodReplacerBeanName; - private List typeIdentifiers = new LinkedList<>(); + private final List typeIdentifiers = new ArrayList<>(); /** @@ -49,7 +49,7 @@ public class ReplaceOverride extends MethodOverride { */ public ReplaceOverride(String methodName, String methodReplacerBeanName) { super(methodName); - Assert.notNull(methodName, "Method replacer bean name must not be null"); + Assert.notNull(methodReplacerBeanName, "Method replacer bean name must not be null"); this.methodReplacerBeanName = methodReplacerBeanName; } @@ -70,6 +70,7 @@ public void addTypeIdentifier(String identifier) { this.typeIdentifiers.add(identifier); } + @Override public boolean matches(Method method) { if (!method.getName().equals(getMethodName())) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java index 8422ad309ebf..863873499266 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -65,7 +65,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition { boolean allowCaching = true; - boolean isFactoryMethodUnique = false; + boolean isFactoryMethodUnique; @Nullable volatile ResolvableType targetType; @@ -86,6 +86,10 @@ public class RootBeanDefinition extends AbstractBeanDefinition { @Nullable volatile Method factoryMethodToIntrospect; + /** Package-visible field for caching a resolved destroy method name (also for inferred). */ + @Nullable + volatile String resolvedDestroyMethodName; + /** Common lock for the four constructor fields below. */ final Object constructorArgumentLock = new Object(); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ScopeNotActiveException.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ScopeNotActiveException.java new file mode 100644 index 000000000000..bb7cddaf2a21 --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ScopeNotActiveException.java @@ -0,0 +1,45 @@ +/* + * 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.beans.factory.support; + +import org.springframework.beans.factory.BeanCreationException; + +/** + * A subclass of {@link BeanCreationException} which indicates that the target scope + * is not active, e.g. in case of request or session scope. + * + * @author Juergen Hoeller + * @since 5.3 + * @see org.springframework.beans.factory.BeanFactory#getBean + * @see org.springframework.beans.factory.config.Scope + * @see AbstractBeanDefinition#setScope + */ +@SuppressWarnings("serial") +public class ScopeNotActiveException extends BeanCreationException { + + /** + * Create a new ScopeNotActiveException. + * @param beanName the name of the bean requested + * @param scopeName the name of the target scope + * @param cause the root cause, typically from {@link org.springframework.beans.factory.config.Scope#get} + */ + public ScopeNotActiveException(String beanName, String scopeName, IllegalStateException cause) { + super(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + + "defining a scoped proxy for this bean if you intend to refer to it from a singleton", cause); + } + +} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleAutowireCandidateResolver.java index 17909b5946b9..f33eeecf16aa 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleAutowireCandidateResolver.java @@ -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. @@ -30,6 +30,13 @@ */ public class SimpleAutowireCandidateResolver implements AutowireCandidateResolver { + /** + * Shared instance of {@code SimpleAutowireCandidateResolver}. + * @since 5.2.7 + */ + public static final SimpleAutowireCandidateResolver INSTANCE = new SimpleAutowireCandidateResolver(); + + @Override public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) { return bdHolder.getBeanDefinition().isAutowireCandidate(); @@ -40,6 +47,11 @@ public boolean isRequired(DependencyDescriptor descriptor) { return descriptor.isRequired(); } + @Override + public boolean hasQualifier(DependencyDescriptor descriptor) { + return false; + } + @Override @Nullable public Object getSuggestedValue(DependencyDescriptor descriptor) { @@ -52,4 +64,13 @@ public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, return null; } + /** + * This implementation returns {@code this} as-is. + * @see #INSTANCE + */ + @Override + public AutowireCandidateResolver cloneIfNecessary() { + return this; + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java index 7d77a45b0d71..a5430120dfdb 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java @@ -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. @@ -37,7 +37,7 @@ import org.springframework.beans.factory.SmartFactoryBean; import org.springframework.core.OrderComparator; import org.springframework.core.ResolvableType; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -45,20 +45,22 @@ /** * Static {@link org.springframework.beans.factory.BeanFactory} implementation - * which allows to register existing singleton instances programmatically. - * Does not have support for prototype beans or aliases. + * which allows one to register existing singleton instances programmatically. * - *

Serves as example for a simple implementation of the + *

Does not have support for prototype beans or aliases. + * + *

Serves as an example for a simple implementation of the * {@link org.springframework.beans.factory.ListableBeanFactory} interface, * managing existing bean instances rather than creating new ones based on bean * definitions, and not implementing any extended SPI interfaces (such as * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}). * - *

For a full-fledged factory based on bean definitions, have a look - * at {@link DefaultListableBeanFactory}. + *

For a full-fledged factory based on bean definitions, have a look at + * {@link DefaultListableBeanFactory}. * * @author Rod Johnson * @author Juergen Hoeller + * @author Sam Brannen * @since 06.01.2003 * @see DefaultListableBeanFactory */ @@ -83,7 +85,7 @@ public StaticListableBeanFactory() { * or {@link java.util.Collections#emptyMap()} for a dummy factory which * enforces operating against an empty set of beans. * @param beans a {@code Map} for holding this factory's beans, with the - * bean name String as key and the corresponding singleton object as value + * bean name as key and the corresponding singleton object as value * @since 4.3 */ public StaticListableBeanFactory(Map beans) { @@ -94,7 +96,7 @@ public StaticListableBeanFactory(Map beans) { /** * Add a new singleton bean. - * Will overwrite any existing instance for the given name. + *

Will overwrite any existing instance for the given name. * @param name the name of the bean * @param bean the bean instance */ @@ -184,73 +186,12 @@ public T getBean(Class requiredType, Object... args) throws BeansExceptio @Override public ObjectProvider getBeanProvider(Class requiredType) throws BeansException { - return getBeanProvider(ResolvableType.forRawClass(requiredType)); + return getBeanProvider(ResolvableType.forRawClass(requiredType), true); } - @SuppressWarnings("unchecked") @Override public ObjectProvider getBeanProvider(ResolvableType requiredType) { - return new ObjectProvider() { - @Override - public T getObject() throws BeansException { - String[] beanNames = getBeanNamesForType(requiredType); - if (beanNames.length == 1) { - return (T) getBean(beanNames[0], requiredType); - } - else if (beanNames.length > 1) { - throw new NoUniqueBeanDefinitionException(requiredType, beanNames); - } - else { - throw new NoSuchBeanDefinitionException(requiredType); - } - } - @Override - public T getObject(Object... args) throws BeansException { - String[] beanNames = getBeanNamesForType(requiredType); - if (beanNames.length == 1) { - return (T) getBean(beanNames[0], args); - } - else if (beanNames.length > 1) { - throw new NoUniqueBeanDefinitionException(requiredType, beanNames); - } - else { - throw new NoSuchBeanDefinitionException(requiredType); - } - } - @Override - @Nullable - public T getIfAvailable() throws BeansException { - String[] beanNames = getBeanNamesForType(requiredType); - if (beanNames.length == 1) { - return (T) getBean(beanNames[0]); - } - else if (beanNames.length > 1) { - throw new NoUniqueBeanDefinitionException(requiredType, beanNames); - } - else { - return null; - } - } - @Override - @Nullable - public T getIfUnique() throws BeansException { - String[] beanNames = getBeanNamesForType(requiredType); - if (beanNames.length == 1) { - return (T) getBean(beanNames[0]); - } - else { - return null; - } - } - @Override - public Stream stream() { - return Arrays.stream(getBeanNamesForType(requiredType)).map(name -> (T) getBean(name)); - } - @Override - public Stream orderedStream() { - return stream().sorted(OrderComparator.INSTANCE); - } - }; + return getBeanProvider(requiredType, true); } @Override @@ -262,7 +203,10 @@ public boolean containsBean(String name) { public boolean isSingleton(String name) throws NoSuchBeanDefinitionException { Object bean = getBean(name); // In case of FactoryBean, return singleton status of created object. - return (bean instanceof FactoryBean && ((FactoryBean) bean).isSingleton()); + if (bean instanceof FactoryBean) { + return ((FactoryBean) bean).isSingleton(); + } + return true; } @Override @@ -332,12 +276,82 @@ public String[] getBeanDefinitionNames() { return StringUtils.toStringArray(this.beans.keySet()); } + @Override + public ObjectProvider getBeanProvider(Class requiredType, boolean allowEagerInit) { + return getBeanProvider(ResolvableType.forRawClass(requiredType), allowEagerInit); + } + + @SuppressWarnings("unchecked") + @Override + public ObjectProvider getBeanProvider(ResolvableType requiredType, boolean allowEagerInit) { + return new ObjectProvider() { + @Override + public T getObject() throws BeansException { + String[] beanNames = getBeanNamesForType(requiredType); + if (beanNames.length == 1) { + return (T) getBean(beanNames[0], requiredType); + } + else if (beanNames.length > 1) { + throw new NoUniqueBeanDefinitionException(requiredType, beanNames); + } + else { + throw new NoSuchBeanDefinitionException(requiredType); + } + } + @Override + public T getObject(Object... args) throws BeansException { + String[] beanNames = getBeanNamesForType(requiredType); + if (beanNames.length == 1) { + return (T) getBean(beanNames[0], args); + } + else if (beanNames.length > 1) { + throw new NoUniqueBeanDefinitionException(requiredType, beanNames); + } + else { + throw new NoSuchBeanDefinitionException(requiredType); + } + } + @Override + @Nullable + public T getIfAvailable() throws BeansException { + String[] beanNames = getBeanNamesForType(requiredType); + if (beanNames.length == 1) { + return (T) getBean(beanNames[0]); + } + else if (beanNames.length > 1) { + throw new NoUniqueBeanDefinitionException(requiredType, beanNames); + } + else { + return null; + } + } + @Override + @Nullable + public T getIfUnique() throws BeansException { + String[] beanNames = getBeanNamesForType(requiredType); + if (beanNames.length == 1) { + return (T) getBean(beanNames[0]); + } + else { + return null; + } + } + @Override + public Stream stream() { + return Arrays.stream(getBeanNamesForType(requiredType)).map(name -> (T) getBean(name)); + } + @Override + public Stream orderedStream() { + return stream().sorted(OrderComparator.INSTANCE); + } + }; + } + @Override public String[] getBeanNamesForType(@Nullable ResolvableType type) { return getBeanNamesForType(type, true, true); } - @Override public String[] getBeanNamesForType(@Nullable ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) { @@ -446,7 +460,7 @@ public A findAnnotationOnBean(String beanName, Class a throws NoSuchBeanDefinitionException { Class beanType = getType(beanName); - return (beanType != null ? AnnotationUtils.findAnnotation(beanType, annotationType) : null); + return (beanType != null ? AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType) : null); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java index 4fd5ccdd6011..6be311eb1efe 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java @@ -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. @@ -90,7 +90,7 @@ public class BeanDefinitionParserDelegate { /** * Value of a T/F attribute that represents true. - * Anything else represents false. Case seNsItive. + * Anything else represents false. */ public static final String TRUE_VALUE = "true"; @@ -1523,7 +1523,7 @@ public boolean nodeNameEquals(Node node, String desiredName) { * Determine whether the given URI indicates the default namespace. */ public boolean isDefaultNamespace(@Nullable String namespaceUri) { - return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri)); + return !StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri); } /** @@ -1534,7 +1534,7 @@ public boolean isDefaultNamespace(Node node) { } private boolean isDefaultValue(String value) { - return (DEFAULT_VALUE.equals(value) || "".equals(value)); + return !StringUtils.hasLength(value) || DEFAULT_VALUE.equals(value); } private boolean isCandidateElement(Node node) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java index 05501d3ce0dd..589208a4d3af 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.java @@ -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. @@ -128,7 +128,12 @@ public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { private final XmlValidationModeDetector validationModeDetector = new XmlValidationModeDetector(); private final ThreadLocal> resourcesCurrentlyBeingLoaded = - new NamedThreadLocal<>("XML bean definition resources currently being loaded"); + new NamedThreadLocal>("XML bean definition resources currently being loaded"){ + @Override + protected Set initialValue() { + return new HashSet<>(4); + } + }; /** @@ -319,26 +324,18 @@ public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefin } Set currentResources = this.resourcesCurrentlyBeingLoaded.get(); - if (currentResources == null) { - currentResources = new HashSet<>(4); - this.resourcesCurrentlyBeingLoaded.set(currentResources); - } + if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } - try { - InputStream inputStream = encodedResource.getResource().getInputStream(); - try { - InputSource inputSource = new InputSource(inputStream); - if (encodedResource.getEncoding() != null) { - inputSource.setEncoding(encodedResource.getEncoding()); - } - return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); - } - finally { - inputStream.close(); + + try (InputStream inputStream = encodedResource.getResource().getInputStream()) { + InputSource inputSource = new InputSource(inputStream); + if (encodedResource.getEncoding() != null) { + inputSource.setEncoding(encodedResource.getEncoding()); } + return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } catch (IOException ex) { throw new BeanDefinitionStoreException( diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CharacterEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CharacterEditor.java index fe485cee6f83..ec7c7d4c9b2c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CharacterEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CharacterEditor.java @@ -82,7 +82,7 @@ else if (isUnicodeCharacterSequence(text)) { setAsUnicode(text); } else if (text.length() == 1) { - setValue(Character.valueOf(text.charAt(0))); + setValue(text.charAt(0)); } else { throw new IllegalArgumentException("String [" + text + "] with length " + @@ -103,7 +103,7 @@ private boolean isUnicodeCharacterSequence(String sequence) { private void setAsUnicode(String text) { int code = Integer.parseInt(text.substring(UNICODE_PREFIX.length()), 16); - setValue(Character.valueOf((char) code)); + setValue((char) code); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/LocaleEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/LocaleEditor.java index 29bf43af3856..5a327f710e0c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/LocaleEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/LocaleEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,9 +23,9 @@ /** * Editor for {@code java.util.Locale}, to directly populate a Locale property. * - *

Expects the same syntax as Locale's {@code toString}, i.e. language + + *

Expects the same syntax as Locale's {@code toString()}, i.e. language + * optionally country + optionally variant, separated by "_" (e.g. "en", "en_US"). - * Also accepts spaces as separators, as alternative to underscores. + * Also accepts spaces as separators, as an alternative to underscores. * * @author Juergen Hoeller * @since 26.05.2003 diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/PathEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/PathEditor.java index f1edae00c793..0ee0f5e80f60 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/PathEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/PathEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,8 +26,8 @@ import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceEditor; -import org.springframework.core.io.ResourceLoader; import org.springframework.util.Assert; +import org.springframework.util.ResourceUtils; /** * Editor for {@code java.nio.file.Path}, to directly populate a Path @@ -74,7 +74,7 @@ public PathEditor(ResourceEditor resourceEditor) { @Override public void setAsText(String text) throws IllegalArgumentException { - boolean nioPathCandidate = !text.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX); + boolean nioPathCandidate = !text.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX); if (nioPathCandidate && !text.startsWith("/")) { try { URI uri = new URI(text); @@ -85,9 +85,13 @@ public void setAsText(String text) throws IllegalArgumentException { return; } } - catch (URISyntaxException | FileSystemNotFoundException ex) { - // Not a valid URI (let's try as Spring resource location), - // or a URI scheme not registered for NIO (let's try URL + catch (URISyntaxException ex) { + // Not a valid URI; potentially a Windows-style path after + // a file prefix (let's try as Spring resource location) + nioPathCandidate = !text.startsWith(ResourceUtils.FILE_URL_PREFIX); + } + catch (FileSystemNotFoundException ex) { + // URI scheme not registered for NIO (let's try URL // protocol handlers via Spring's resource mechanism). } } @@ -97,7 +101,7 @@ public void setAsText(String text) throws IllegalArgumentException { if (resource == null) { setValue(null); } - else if (!resource.exists() && nioPathCandidate) { + else if (nioPathCandidate && !resource.exists()) { setValue(Paths.get(text).normalize()); } else { diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/StringArrayPropertyEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/StringArrayPropertyEditor.java index eee219ed9e8f..1a7a8ccc24f8 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/StringArrayPropertyEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/StringArrayPropertyEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -53,7 +53,7 @@ public class StringArrayPropertyEditor extends PropertyEditorSupport { /** - * Create a new StringArrayPropertyEditor with the default separator + * Create a new {@code StringArrayPropertyEditor} with the default separator * (a comma). *

An empty text (without elements) will be turned into an empty array. */ @@ -62,7 +62,7 @@ public StringArrayPropertyEditor() { } /** - * Create a new StringArrayPropertyEditor with the given separator. + * Create a new {@code StringArrayPropertyEditor} with the given separator. *

An empty text (without elements) will be turned into an empty array. * @param separator the separator to use for splitting a {@link String} */ @@ -71,7 +71,7 @@ public StringArrayPropertyEditor(String separator) { } /** - * Create a new StringArrayPropertyEditor with the given separator. + * Create a new {@code StringArrayPropertyEditor} with the given separator. * @param separator the separator to use for splitting a {@link String} * @param emptyArrayAsNull {@code true} if an empty String array * is to be transformed into {@code null} @@ -81,19 +81,19 @@ public StringArrayPropertyEditor(String separator, boolean emptyArrayAsNull) { } /** - * Create a new StringArrayPropertyEditor with the given separator. + * Create a new {@code StringArrayPropertyEditor} with the given separator. * @param separator the separator to use for splitting a {@link String} * @param emptyArrayAsNull {@code true} if an empty String array * is to be transformed into {@code null} * @param trimValues {@code true} if the values in the parsed arrays - * are to be trimmed of whitespace (default is true). + * are to be trimmed of whitespace (default is true) */ public StringArrayPropertyEditor(String separator, boolean emptyArrayAsNull, boolean trimValues) { this(separator, null, emptyArrayAsNull, trimValues); } /** - * Create a new StringArrayPropertyEditor with the given separator. + * Create a new {@code StringArrayPropertyEditor} with the given separator. * @param separator the separator to use for splitting a {@link String} * @param charsToDelete a set of characters to delete, in addition to * trimming an input String. Useful for deleting unwanted line breaks: @@ -106,7 +106,7 @@ public StringArrayPropertyEditor(String separator, @Nullable String charsToDelet } /** - * Create a new StringArrayPropertyEditor with the given separator. + * Create a new {@code StringArrayPropertyEditor} with the given separator. * @param separator the separator to use for splitting a {@link String} * @param charsToDelete a set of characters to delete, in addition to * trimming an input String. Useful for deleting unwanted line breaks: @@ -114,7 +114,7 @@ public StringArrayPropertyEditor(String separator, @Nullable String charsToDelet * @param emptyArrayAsNull {@code true} if an empty String array * is to be transformed into {@code null} * @param trimValues {@code true} if the values in the parsed arrays - * are to be trimmed of whitespace (default is true). + * are to be trimmed of whitespace (default is true) */ public StringArrayPropertyEditor( String separator, @Nullable String charsToDelete, boolean emptyArrayAsNull, boolean trimValues) { @@ -128,13 +128,13 @@ public StringArrayPropertyEditor( @Override public void setAsText(String text) throws IllegalArgumentException { String[] array = StringUtils.delimitedListToStringArray(text, this.separator, this.charsToDelete); - if (this.trimValues) { - array = StringUtils.trimArrayElements(array); - } if (this.emptyArrayAsNull && array.length == 0) { setValue(null); } else { + if (this.trimValues) { + array = StringUtils.trimArrayElements(array); + } setValue(array); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URIEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URIEditor.java index 8327653c456a..344fb5d439f9 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URIEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URIEditor.java @@ -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. @@ -119,7 +119,7 @@ public void setAsText(String text) throws IllegalArgumentException { setValue(createURI(uri)); } catch (URISyntaxException ex) { - throw new IllegalArgumentException("Invalid URI syntax: " + ex); + throw new IllegalArgumentException("Invalid URI syntax: " + ex.getMessage()); } } } diff --git a/spring-beans/src/main/java/org/springframework/beans/support/ArgumentConvertingMethodInvoker.java b/spring-beans/src/main/java/org/springframework/beans/support/ArgumentConvertingMethodInvoker.java index 006da60e4d03..bdd72f24d380 100644 --- a/spring-beans/src/main/java/org/springframework/beans/support/ArgumentConvertingMethodInvoker.java +++ b/spring-beans/src/main/java/org/springframework/beans/support/ArgumentConvertingMethodInvoker.java @@ -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. @@ -146,8 +146,9 @@ protected Method doFindMatchingMethod(Object[] arguments) { for (Method candidate : candidates) { if (candidate.getName().equals(targetMethod)) { // Check if the inspected method has the correct number of parameters. - Class[] paramTypes = candidate.getParameterTypes(); - if (paramTypes.length == argCount) { + int parameterCount = candidate.getParameterCount(); + if (parameterCount == argCount) { + Class[] paramTypes = candidate.getParameterTypes(); Object[] convertedArguments = new Object[argCount]; boolean match = true; for (int j = 0; j < argCount && match; j++) { diff --git a/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java b/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java index 0b7576bad4ca..0bb5febde05f 100644 --- a/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java @@ -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. @@ -25,7 +25,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; @@ -35,34 +34,28 @@ import java.util.TreeMap; import java.util.TreeSet; -import org.apache.commons.logging.LogFactory; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowire; -import org.springframework.beans.propertyeditors.CustomNumberEditor; import org.springframework.beans.propertyeditors.StringArrayPropertyEditor; import org.springframework.beans.propertyeditors.StringTrimmerEditor; import org.springframework.beans.support.DerivedFromProtectedBaseBean; +import org.springframework.beans.testfixture.beans.BooleanTestBean; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.IndexedTestBean; +import org.springframework.beans.testfixture.beans.NumberTestBean; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.lang.Nullable; -import org.springframework.tests.Assume; -import org.springframework.tests.EnabledForTestGroups; -import org.springframework.tests.sample.beans.BooleanTestBean; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.IndexedTestBean; -import org.springframework.tests.sample.beans.NumberTestBean; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.util.StopWatch; import org.springframework.util.StringUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.within; -import static org.springframework.tests.TestGroup.PERFORMANCE; /** * Shared tests for property accessors. @@ -300,7 +293,7 @@ public void setNestedPropertyPolymorphic() throws Exception { AbstractPropertyAccessor accessor = createAccessor(target); accessor.setPropertyValue("spouse", kerry); - accessor.setPropertyValue("spouse.age", new Integer(35)); + accessor.setPropertyValue("spouse.age", 35); accessor.setPropertyValue("spouse.name", "Kerry"); accessor.setPropertyValue("spouse.company", "Lewisham"); assertThat(kerry.getName().equals("Kerry")).as("kerry name is Kerry").isTrue(); @@ -389,7 +382,7 @@ public void setAnotherPropertyIntermediatePropertyIsNull() throws Exception { ITestBean target = new TestBean("rod", 31); AbstractPropertyAccessor accessor = createAccessor(target); assertThatExceptionOfType(NullValueInNestedPathException.class).isThrownBy(() -> - accessor.setPropertyValue("spouse.age", new Integer(31))) + accessor.setPropertyValue("spouse.age", 31)) .satisfies(ex -> assertThat(ex.getPropertyName()).isEqualTo("spouse")); } @@ -482,7 +475,7 @@ public void setIndividualValidPropertyValues() { int newAge = 65; String newTouchy = "valid"; AbstractPropertyAccessor accessor = createAccessor(target); - accessor.setPropertyValue("age", new Integer(newAge)); + accessor.setPropertyValue("age", newAge); accessor.setPropertyValue(new PropertyValue("name", newName)); accessor.setPropertyValue(new PropertyValue("touchy", newTouchy)); assertThat(target.getName().equals(newName)).as("Name property should have changed").isTrue(); @@ -618,13 +611,13 @@ public void setNumberProperties() { public void setNumberPropertiesWithCoercion() { NumberTestBean target = new NumberTestBean(); AbstractPropertyAccessor accessor = createAccessor(target); - accessor.setPropertyValue("short2", new Integer(2)); - accessor.setPropertyValue("int2", new Long(8)); + accessor.setPropertyValue("short2", 2); + accessor.setPropertyValue("int2", 8L); accessor.setPropertyValue("long2", new BigInteger("6")); - accessor.setPropertyValue("bigInteger", new Integer(3)); - accessor.setPropertyValue("float2", new Double(8.1)); + accessor.setPropertyValue("bigInteger", 3L); + accessor.setPropertyValue("float2", 8.1D); accessor.setPropertyValue("double2", new BigDecimal(6.1)); - accessor.setPropertyValue("bigDecimal", new Float(4.0)); + accessor.setPropertyValue("bigDecimal", 4.0F); assertThat(new Short("2").equals(accessor.getPropertyValue("short2"))).as("Correct short2 value").isTrue(); assertThat(new Short("2").equals(target.getShort2())).as("Correct short2 value").isTrue(); assertThat(new Integer("8").equals(accessor.getPropertyValue("int2"))).as("Correct int2 value").isTrue(); @@ -886,14 +879,14 @@ public void setIntArrayProperty() { result.add(target.intArray[0]); result.add(target.intArray[1]); result.add(target.intArray[2]); - assertThat(result.contains(new Integer(4)) && result.contains(new Integer(5)) && - result.contains(new Integer(3))).as("correct values").isTrue(); + assertThat(result.contains(4) && result.contains(5) && + result.contains(3)).as("correct values").isTrue(); accessor.setPropertyValue("intArray", new Integer[] {1}); assertThat(target.intArray.length == 1).as("intArray length = 4").isTrue(); assertThat(target.intArray[0] == 1).as("correct values").isTrue(); - accessor.setPropertyValue("intArray", new Integer(1)); + accessor.setPropertyValue("intArray", 1); assertThat(target.intArray.length == 1).as("intArray length = 4").isTrue(); assertThat(target.intArray[0] == 1).as("correct values").isTrue(); @@ -913,7 +906,7 @@ public void setIntArrayPropertyWithCustomEditor() { accessor.registerCustomEditor(int.class, new PropertyEditorSupport() { @Override public void setAsText(String text) { - setValue(new Integer(Integer.parseInt(text) + 1)); + setValue(Integer.parseInt(text) + 1); } }); @@ -927,7 +920,7 @@ public void setAsText(String text) { assertThat(target.intArray[0] == 4 && target.intArray[1] == 5 && target.intArray[2] == 2 && target.intArray[3] == 3).as("correct values").isTrue(); - accessor.setPropertyValue("intArray", new Integer(1)); + accessor.setPropertyValue("intArray", 1); assertThat(target.intArray.length == 1).as("intArray length = 4").isTrue(); assertThat(target.intArray[0] == 1).as("correct values").isTrue(); @@ -960,60 +953,6 @@ public void setPrimitiveArrayProperty() { assertThat(target.getArray()[1]).isEqualTo(2); } - @Test - @EnabledForTestGroups(PERFORMANCE) - public void setPrimitiveArrayPropertyLargeMatching() { - Assume.notLogging(LogFactory.getLog(AbstractPropertyAccessorTests.class)); - - PrimitiveArrayBean target = new PrimitiveArrayBean(); - AbstractPropertyAccessor accessor = createAccessor(target); - int[] input = new int[1024]; - StopWatch sw = new StopWatch(); - sw.start("array1"); - for (int i = 0; i < 1000; i++) { - accessor.setPropertyValue("array", input); - } - sw.stop(); - assertThat(target.getArray().length).isEqualTo(1024); - assertThat(target.getArray()[0]).isEqualTo(0); - long time1 = sw.getLastTaskTimeMillis(); - assertThat(sw.getLastTaskTimeMillis() < 100).as("Took too long").isTrue(); - - accessor.registerCustomEditor(String.class, new StringTrimmerEditor(false)); - sw.start("array2"); - for (int i = 0; i < 1000; i++) { - accessor.setPropertyValue("array", input); - } - sw.stop(); - assertThat(sw.getLastTaskTimeMillis() < 125).as("Took too long").isTrue(); - - accessor.registerCustomEditor(int.class, "array.somePath", new CustomNumberEditor(Integer.class, false)); - sw.start("array3"); - for (int i = 0; i < 1000; i++) { - accessor.setPropertyValue("array", input); - } - sw.stop(); - assertThat(sw.getLastTaskTimeMillis() < 100).as("Took too long").isTrue(); - - accessor.registerCustomEditor(int.class, "array[0].somePath", new CustomNumberEditor(Integer.class, false)); - sw.start("array3"); - for (int i = 0; i < 1000; i++) { - accessor.setPropertyValue("array", input); - } - sw.stop(); - assertThat(sw.getLastTaskTimeMillis() < 100).as("Took too long").isTrue(); - - accessor.registerCustomEditor(int.class, new CustomNumberEditor(Integer.class, false)); - sw.start("array4"); - for (int i = 0; i < 100; i++) { - accessor.setPropertyValue("array", input); - } - sw.stop(); - assertThat(target.getArray().length).isEqualTo(1024); - assertThat(target.getArray()[0]).isEqualTo(0); - assertThat(sw.getLastTaskTimeMillis() > time1).as("Took too long").isTrue(); - } - @Test public void setPrimitiveArrayPropertyLargeMatchingWithSpecificEditor() { PrimitiveArrayBean target = new PrimitiveArrayBean(); @@ -1022,7 +961,7 @@ public void setPrimitiveArrayPropertyLargeMatchingWithSpecificEditor() { @Override public void setValue(Object value) { if (value instanceof Integer) { - super.setValue(new Integer((Integer) value + 1)); + super.setValue((Integer) value + 1); } } }); @@ -1041,7 +980,7 @@ public void setPrimitiveArrayPropertyLargeMatchingWithIndexSpecificEditor() { @Override public void setValue(Object value) { if (value instanceof Integer) { - super.setValue(new Integer((Integer) value + 1)); + super.setValue((Integer) value + 1); } } }); @@ -1072,7 +1011,7 @@ public void setPrimitiveArrayPropertyWithAutoGrow() throws Exception { public void setGenericArrayProperty() { SkipReaderStub target = new SkipReaderStub(); AbstractPropertyAccessor accessor = createAccessor(target); - List values = new LinkedList<>(); + List values = new ArrayList<>(); values.add("1"); values.add("2"); values.add("3"); @@ -1113,7 +1052,7 @@ public void setCollectionProperty() { SortedSet sortedSet = new TreeSet<>(); sortedSet.add("sortedSet1"); accessor.setPropertyValue("sortedSet", sortedSet); - List list = new LinkedList<>(); + List list = new ArrayList<>(); list.add("list1"); accessor.setPropertyValue("list", list); assertThat(target.getCollection()).isSameAs(coll); @@ -1130,7 +1069,7 @@ public void setCollectionPropertyNonMatchingType() { Collection coll = new ArrayList<>(); coll.add("coll1"); accessor.setPropertyValue("collection", coll); - List set = new LinkedList<>(); + List set = new ArrayList<>(); set.add("set1"); accessor.setPropertyValue("set", set); List sortedSet = new ArrayList<>(); @@ -1157,7 +1096,7 @@ public void setCollectionPropertyWithArrayValue() { Collection coll = new HashSet<>(); coll.add("coll1"); accessor.setPropertyValue("collection", coll.toArray()); - List set = new LinkedList<>(); + List set = new ArrayList<>(); set.add("set1"); accessor.setPropertyValue("set", set.toArray()); List sortedSet = new ArrayList<>(); @@ -1184,7 +1123,7 @@ public void setCollectionPropertyWithIntArrayValue() { Collection coll = new HashSet<>(); coll.add(0); accessor.setPropertyValue("collection", new int[] {0}); - List set = new LinkedList<>(); + List set = new ArrayList<>(); set.add(1); accessor.setPropertyValue("set", new int[] {1}); List sortedSet = new ArrayList<>(); @@ -1210,16 +1149,16 @@ public void setCollectionPropertyWithIntegerValue() { AbstractPropertyAccessor accessor = createAccessor(target); Collection coll = new HashSet<>(); coll.add(0); - accessor.setPropertyValue("collection", new Integer(0)); - List set = new LinkedList<>(); + accessor.setPropertyValue("collection", 0); + List set = new ArrayList<>(); set.add(1); - accessor.setPropertyValue("set", new Integer(1)); + accessor.setPropertyValue("set", 1); List sortedSet = new ArrayList<>(); sortedSet.add(2); - accessor.setPropertyValue("sortedSet", new Integer(2)); + accessor.setPropertyValue("sortedSet", 2); Set list = new HashSet<>(); list.add(3); - accessor.setPropertyValue("list", new Integer(3)); + accessor.setPropertyValue("list", 3); assertThat(target.getCollection().size()).isEqualTo(1); assertThat(target.getCollection().containsAll(coll)).isTrue(); assertThat(target.getSet().size()).isEqualTo(1); @@ -1235,7 +1174,7 @@ public void setCollectionPropertyWithIntegerValue() { public void setCollectionPropertyWithStringValue() { IndexedTestBean target = new IndexedTestBean(); AbstractPropertyAccessor accessor = createAccessor(target); - List set = new LinkedList<>(); + List set = new ArrayList<>(); set.add("set1"); accessor.setPropertyValue("set", "set1"); List sortedSet = new ArrayList<>(); diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanUtilsTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanUtilsTests.java index 544d06750a92..f3e9d2eaa9a1 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanUtilsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,11 +19,16 @@ import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.net.URI; import java.net.URL; import java.time.DayOfWeek; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Locale; @@ -34,12 +39,12 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.propertyeditors.CustomDateEditor; +import org.springframework.beans.testfixture.beans.DerivedTestBean; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceEditor; import org.springframework.lang.Nullable; -import org.springframework.tests.sample.beans.DerivedTestBean; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -58,19 +63,19 @@ class BeanUtilsTests { @Test - void testInstantiateClassGivenInterface() { + void instantiateClassGivenInterface() { assertThatExceptionOfType(FatalBeanException.class).isThrownBy(() -> BeanUtils.instantiateClass(List.class)); } @Test - void testInstantiateClassGivenClassWithoutDefaultConstructor() { + void instantiateClassGivenClassWithoutDefaultConstructor() { assertThatExceptionOfType(FatalBeanException.class).isThrownBy(() -> BeanUtils.instantiateClass(CustomDateEditor.class)); } @Test // gh-22531 - void testInstantiateClassWithOptionalNullableType() throws NoSuchMethodException { + void instantiateClassWithOptionalNullableType() throws NoSuchMethodException { Constructor ctor = BeanWithNullableTypes.class.getDeclaredConstructor( Integer.class, Boolean.class, String.class); BeanWithNullableTypes bean = BeanUtils.instantiateClass(ctor, null, null, "foo"); @@ -80,7 +85,7 @@ void testInstantiateClassWithOptionalNullableType() throws NoSuchMethodException } @Test // gh-22531 - void testInstantiateClassWithOptionalPrimitiveType() throws NoSuchMethodException { + void instantiateClassWithOptionalPrimitiveType() throws NoSuchMethodException { Constructor ctor = BeanWithPrimitiveTypes.class.getDeclaredConstructor(int.class, boolean.class, String.class); BeanWithPrimitiveTypes bean = BeanUtils.instantiateClass(ctor, null, null, "foo"); assertThat(bean.getCounter()).isEqualTo(0); @@ -88,15 +93,21 @@ void testInstantiateClassWithOptionalPrimitiveType() throws NoSuchMethodExceptio assertThat(bean.getValue()).isEqualTo("foo"); } - @Test // gh-22531 - void testInstantiateClassWithMoreArgsThanParameters() throws NoSuchMethodException { + @Test // gh-22531 + void instantiateClassWithMoreArgsThanParameters() throws NoSuchMethodException { Constructor ctor = BeanWithPrimitiveTypes.class.getDeclaredConstructor(int.class, boolean.class, String.class); assertThatExceptionOfType(BeanInstantiationException.class).isThrownBy(() -> BeanUtils.instantiateClass(ctor, null, null, "foo", null)); } @Test - void testGetPropertyDescriptors() throws Exception { + void instantiatePrivateClassWithPrivateConstructor() throws NoSuchMethodException { + Constructor ctor = PrivateBeanWithPrivateConstructor.class.getDeclaredConstructor(); + BeanUtils.instantiateClass(ctor); + } + + @Test + void getPropertyDescriptors() throws Exception { PropertyDescriptor[] actual = Introspector.getBeanInfo(TestBean.class).getPropertyDescriptors(); PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(TestBean.class); assertThat(descriptors).as("Descriptors should not be null").isNotNull(); @@ -104,7 +115,7 @@ void testGetPropertyDescriptors() throws Exception { } @Test - void testBeanPropertyIsArray() { + void beanPropertyIsArray() { PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(ContainerBean.class); for (PropertyDescriptor descriptor : descriptors) { if ("containedBeans".equals(descriptor.getName())) { @@ -115,12 +126,12 @@ void testBeanPropertyIsArray() { } @Test - void testFindEditorByConvention() { + void findEditorByConvention() { assertThat(BeanUtils.findEditorByConvention(Resource.class).getClass()).isEqualTo(ResourceEditor.class); } @Test - void testCopyProperties() throws Exception { + void copyProperties() throws Exception { TestBean tb = new TestBean(); tb.setName("rod"); tb.setAge(32); @@ -136,7 +147,7 @@ void testCopyProperties() throws Exception { } @Test - void testCopyPropertiesWithDifferentTypes1() throws Exception { + void copyPropertiesWithDifferentTypes1() throws Exception { DerivedTestBean tb = new DerivedTestBean(); tb.setName("rod"); tb.setAge(32); @@ -152,7 +163,7 @@ void testCopyPropertiesWithDifferentTypes1() throws Exception { } @Test - void testCopyPropertiesWithDifferentTypes2() throws Exception { + void copyPropertiesWithDifferentTypes2() throws Exception { TestBean tb = new TestBean(); tb.setName("rod"); tb.setAge(32); @@ -168,7 +179,52 @@ void testCopyPropertiesWithDifferentTypes2() throws Exception { } @Test - void testCopyPropertiesWithEditable() throws Exception { + void copyPropertiesHonorsGenericTypeMatches() { + IntegerListHolder1 integerListHolder1 = new IntegerListHolder1(); + integerListHolder1.getList().add(42); + IntegerListHolder2 integerListHolder2 = new IntegerListHolder2(); + + BeanUtils.copyProperties(integerListHolder1, integerListHolder2); + assertThat(integerListHolder1.getList()).containsOnly(42); + assertThat(integerListHolder2.getList()).containsOnly(42); + } + + @Test + void copyPropertiesDoesNotHonorGenericTypeMismatches() { + IntegerListHolder1 integerListHolder = new IntegerListHolder1(); + integerListHolder.getList().add(42); + LongListHolder longListHolder = new LongListHolder(); + + BeanUtils.copyProperties(integerListHolder, longListHolder); + assertThat(integerListHolder.getList()).containsOnly(42); + assertThat(longListHolder.getList()).isEmpty(); + } + + @Test // gh-26531 + void copyPropertiesIgnoresGenericsIfSourceOrTargetHasUnresolvableGenerics() throws Exception { + Order original = new Order("test", Arrays.asList("foo", "bar")); + + // Create a Proxy that loses the generic type information for the getLineItems() method. + OrderSummary proxy = proxyOrder(original); + assertThat(OrderSummary.class.getDeclaredMethod("getLineItems").toGenericString()) + .contains("java.util.List"); + assertThat(proxy.getClass().getDeclaredMethod("getLineItems").toGenericString()) + .contains("java.util.List") + .doesNotContain(""); + + // Ensure that our custom Proxy works as expected. + assertThat(proxy.getId()).isEqualTo("test"); + assertThat(proxy.getLineItems()).containsExactly("foo", "bar"); + + // Copy from proxy to target. + Order target = new Order(); + BeanUtils.copyProperties(proxy, target); + assertThat(target.getId()).isEqualTo("test"); + assertThat(target.getLineItems()).containsExactly("foo", "bar"); + } + + @Test + void copyPropertiesWithEditable() throws Exception { TestBean tb = new TestBean(); assertThat(tb.getName() == null).as("Name empty").isTrue(); tb.setAge(32); @@ -186,7 +242,7 @@ void testCopyPropertiesWithEditable() throws Exception { } @Test - void testCopyPropertiesWithIgnore() throws Exception { + void copyPropertiesWithIgnore() throws Exception { TestBean tb = new TestBean(); assertThat(tb.getName() == null).as("Name empty").isTrue(); tb.setAge(32); @@ -204,7 +260,7 @@ void testCopyPropertiesWithIgnore() throws Exception { } @Test - void testCopyPropertiesWithIgnoredNonExistingProperty() { + void copyPropertiesWithIgnoredNonExistingProperty() { NameAndSpecialProperty source = new NameAndSpecialProperty(); source.setName("name"); TestBean target = new TestBean(); @@ -213,7 +269,7 @@ void testCopyPropertiesWithIgnoredNonExistingProperty() { } @Test - void testCopyPropertiesWithInvalidProperty() { + void copyPropertiesWithInvalidProperty() { InvalidProperty source = new InvalidProperty(); source.setName("name"); source.setFlag1(true); @@ -226,39 +282,39 @@ void testCopyPropertiesWithInvalidProperty() { } @Test - void testResolveSimpleSignature() throws Exception { + void resolveSimpleSignature() throws Exception { Method desiredMethod = MethodSignatureBean.class.getMethod("doSomething"); assertSignatureEquals(desiredMethod, "doSomething"); assertSignatureEquals(desiredMethod, "doSomething()"); } @Test - void testResolveInvalidSignatureEndParen() { + void resolveInvalidSignatureEndParen() { assertThatIllegalArgumentException().isThrownBy(() -> BeanUtils.resolveSignature("doSomething(", MethodSignatureBean.class)); } @Test - void testResolveInvalidSignatureStartParen() { + void resolveInvalidSignatureStartParen() { assertThatIllegalArgumentException().isThrownBy(() -> BeanUtils.resolveSignature("doSomething)", MethodSignatureBean.class)); } @Test - void testResolveWithAndWithoutArgList() throws Exception { + void resolveWithAndWithoutArgList() throws Exception { Method desiredMethod = MethodSignatureBean.class.getMethod("doSomethingElse", String.class, int.class); assertSignatureEquals(desiredMethod, "doSomethingElse"); assertThat(BeanUtils.resolveSignature("doSomethingElse()", MethodSignatureBean.class)).isNull(); } @Test - void testResolveTypedSignature() throws Exception { + void resolveTypedSignature() throws Exception { Method desiredMethod = MethodSignatureBean.class.getMethod("doSomethingElse", String.class, int.class); assertSignatureEquals(desiredMethod, "doSomethingElse(java.lang.String, int)"); } @Test - void testResolveOverloadedSignature() throws Exception { + void resolveOverloadedSignature() throws Exception { // test resolve with no args Method desiredMethod = MethodSignatureBean.class.getMethod("overloaded"); assertSignatureEquals(desiredMethod, "overloaded()"); @@ -273,7 +329,7 @@ void testResolveOverloadedSignature() throws Exception { } @Test - void testResolveSignatureWithArray() throws Exception { + void resolveSignatureWithArray() throws Exception { Method desiredMethod = MethodSignatureBean.class.getMethod("doSomethingWithAnArray", String[].class); assertSignatureEquals(desiredMethod, "doSomethingWithAnArray(java.lang.String[])"); @@ -282,7 +338,7 @@ void testResolveSignatureWithArray() throws Exception { } @Test - void testSPR6063() { + void spr6063() { PropertyDescriptor[] descrs = BeanUtils.getPropertyDescriptors(Bean.class); PropertyDescriptor keyDescr = BeanUtils.getPropertyDescriptor(Bean.class, "value"); @@ -332,6 +388,49 @@ private void assertSignatureEquals(Method desiredMethod, String signature) { } + @SuppressWarnings("unused") + private static class IntegerListHolder1 { + + private List list = new ArrayList<>(); + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + } + + @SuppressWarnings("unused") + private static class IntegerListHolder2 { + + private List list = new ArrayList<>(); + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + } + + @SuppressWarnings("unused") + private static class LongListHolder { + + private List list = new ArrayList<>(); + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + } + + @SuppressWarnings("unused") private static class NameAndSpecialProperty { @@ -555,4 +654,83 @@ public String getValue() { } } + private static class PrivateBeanWithPrivateConstructor { + + private PrivateBeanWithPrivateConstructor() { + } + } + + @SuppressWarnings("unused") + private static class Order { + + private String id; + private List lineItems; + + + Order() { + } + + Order(String id, List lineItems) { + this.id = id; + this.lineItems = lineItems; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public List getLineItems() { + return this.lineItems; + } + + public void setLineItems(List lineItems) { + this.lineItems = lineItems; + } + + @Override + public String toString() { + return "Order [id=" + this.id + ", lineItems=" + this.lineItems + "]"; + } + } + + private interface OrderSummary { + + String getId(); + + List getLineItems(); + } + + + private OrderSummary proxyOrder(Order order) { + return (OrderSummary) Proxy.newProxyInstance(getClass().getClassLoader(), + new Class[] { OrderSummary.class }, new OrderInvocationHandler(order)); + } + + + private static class OrderInvocationHandler implements InvocationHandler { + + private final Order order; + + + OrderInvocationHandler(Order order) { + this.order = order; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + try { + // Ignore args since OrderSummary doesn't declare any methods with arguments, + // and we're not supporting equals(Object), etc. + return Order.class.getDeclaredMethod(method.getName()).invoke(this.order); + } + catch (InvocationTargetException ex) { + throw ex.getTargetException(); + } + } + } + } diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperAutoGrowingTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperAutoGrowingTests.java index c17e2c7359b0..2dddf884e319 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperAutoGrowingTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperAutoGrowingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ /** * @author Keith Donald * @author Juergen Hoeller + * @author Sam Brannen */ public class BeanWrapperAutoGrowingTests { @@ -66,11 +67,6 @@ public void getPropertyValueAutoGrowArray() { assertThat(bean.getArray()[0]).isInstanceOf(Bean.class); } - private void assertNotNull(Object propertyValue) { - assertThat(propertyValue).isNotNull(); - } - - @Test public void setPropertyValueAutoGrowArray() { wrapper.setPropertyValue("array[0].prop", "test"); @@ -93,12 +89,39 @@ public void getPropertyValueAutoGrowArrayBySeveralElements() { } @Test - public void getPropertyValueAutoGrowMultiDimensionalArray() { + public void getPropertyValueAutoGrow2dArray() { assertNotNull(wrapper.getPropertyValue("multiArray[0][0]")); assertThat(bean.getMultiArray()[0].length).isEqualTo(1); assertThat(bean.getMultiArray()[0][0]).isInstanceOf(Bean.class); } + @Test + public void getPropertyValueAutoGrow3dArray() { + assertNotNull(wrapper.getPropertyValue("threeDimensionalArray[1][2][3]")); + assertThat(bean.getThreeDimensionalArray()[1].length).isEqualTo(3); + assertThat(bean.getThreeDimensionalArray()[1][2][3]).isInstanceOf(Bean.class); + } + + @Test + public void setPropertyValueAutoGrow2dArray() { + Bean newBean = new Bean(); + newBean.setProp("enigma"); + wrapper.setPropertyValue("multiArray[2][3]", newBean); + assertThat(bean.getMultiArray()[2][3]) + .isInstanceOf(Bean.class) + .extracting(Bean::getProp).isEqualTo("enigma"); + } + + @Test + public void setPropertyValueAutoGrow3dArray() { + Bean newBean = new Bean(); + newBean.setProp("enigma"); + wrapper.setPropertyValue("threeDimensionalArray[2][3][4]", newBean); + assertThat(bean.getThreeDimensionalArray()[2][3][4]) + .isInstanceOf(Bean.class) + .extracting(Bean::getProp).isEqualTo("enigma"); + } + @Test public void getPropertyValueAutoGrowList() { assertNotNull(wrapper.getPropertyValue("list[0]")); @@ -131,7 +154,7 @@ public void getPropertyValueAutoGrowListBySeveralElements() { public void getPropertyValueAutoGrowListFailsAgainstLimit() { wrapper.setAutoGrowCollectionLimit(2); assertThatExceptionOfType(InvalidPropertyException.class).isThrownBy(() -> - assertNotNull(wrapper.getPropertyValue("list[4]"))) + wrapper.getPropertyValue("list[4]")) .withRootCauseInstanceOf(IndexOutOfBoundsException.class); } @@ -161,6 +184,11 @@ public void setNestedPropertyValueAutoGrowMap() { } + private static void assertNotNull(Object propertyValue) { + assertThat(propertyValue).isNotNull(); + } + + @SuppressWarnings("rawtypes") public static class Bean { @@ -174,6 +202,8 @@ public static class Bean { private Bean[][] multiArray; + private Bean[][][] threeDimensionalArray; + private List list; private List> multiList; @@ -214,6 +244,14 @@ public void setMultiArray(Bean[][] multiArray) { this.multiArray = multiArray; } + public Bean[][][] getThreeDimensionalArray() { + return threeDimensionalArray; + } + + public void setThreeDimensionalArray(Bean[][][] threeDimensionalArray) { + this.threeDimensionalArray = threeDimensionalArray; + } + public List getList() { return list; } diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperEnumTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperEnumTests.java index 5327e70a3a45..3a5ac3717127 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperEnumTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperEnumTests.java @@ -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,9 +21,9 @@ import org.junit.jupiter.api.Test; +import org.springframework.beans.testfixture.beans.CustomEnum; +import org.springframework.beans.testfixture.beans.GenericBean; import org.springframework.core.convert.support.DefaultConversionService; -import org.springframework.tests.sample.beans.CustomEnum; -import org.springframework.tests.sample.beans.GenericBean; import static org.assertj.core.api.Assertions.assertThat; @@ -158,8 +158,8 @@ public void testStandardEnumMapWithMultipleValues() { map.put("VALUE_2", 2); bw.setPropertyValue("standardEnumMap", map); assertThat(gb.getStandardEnumMap().size()).isEqualTo(2); - assertThat(gb.getStandardEnumMap().get(CustomEnum.VALUE_1)).isEqualTo(new Integer(1)); - assertThat(gb.getStandardEnumMap().get(CustomEnum.VALUE_2)).isEqualTo(new Integer(2)); + assertThat(gb.getStandardEnumMap().get(CustomEnum.VALUE_1)).isEqualTo(1); + assertThat(gb.getStandardEnumMap().get(CustomEnum.VALUE_2)).isEqualTo(2); } @Test @@ -170,7 +170,7 @@ public void testStandardEnumMapWithAutoGrowing() { assertThat(gb.getStandardEnumMap()).isNull(); bw.setPropertyValue("standardEnumMap[VALUE_1]", 1); assertThat(gb.getStandardEnumMap().size()).isEqualTo(1); - assertThat(gb.getStandardEnumMap().get(CustomEnum.VALUE_1)).isEqualTo(new Integer(1)); + assertThat(gb.getStandardEnumMap().get(CustomEnum.VALUE_1)).isEqualTo(1); } @Test diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperGenericsTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperGenericsTests.java index 9205b2c5f995..be0c2cc5953a 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperGenericsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperGenericsTests.java @@ -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. @@ -23,7 +23,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; @@ -33,11 +32,11 @@ import org.springframework.beans.propertyeditors.CustomNumberEditor; import org.springframework.beans.propertyeditors.StringTrimmerEditor; +import org.springframework.beans.testfixture.beans.GenericBean; +import org.springframework.beans.testfixture.beans.GenericIntegerBean; +import org.springframework.beans.testfixture.beans.GenericSetOfIntegerBean; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.UrlResource; -import org.springframework.tests.sample.beans.GenericBean; -import org.springframework.tests.sample.beans.GenericIntegerBean; -import org.springframework.tests.sample.beans.GenericSetOfIntegerBean; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -57,8 +56,8 @@ public void testGenericSet() { input.add("4"); input.add("5"); bw.setPropertyValue("integerSet", input); - assertThat(gb.getIntegerSet().contains(new Integer(4))).isTrue(); - assertThat(gb.getIntegerSet().contains(new Integer(5))).isTrue(); + assertThat(gb.getIntegerSet().contains(4)).isTrue(); + assertThat(gb.getIntegerSet().contains(5)).isTrue(); } @Test @@ -70,8 +69,8 @@ public void testGenericLowerBoundedSet() { input.add("4"); input.add("5"); bw.setPropertyValue("numberSet", input); - assertThat(gb.getNumberSet().contains(new Integer(4))).isTrue(); - assertThat(gb.getNumberSet().contains(new Integer(5))).isTrue(); + assertThat(gb.getNumberSet().contains(4)).isTrue(); + assertThat(gb.getNumberSet().contains(5)).isTrue(); } @Test @@ -114,8 +113,8 @@ public void testGenericMap() { input.put("4", "5"); input.put("6", "7"); bw.setPropertyValue("shortMap", input); - assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(new Integer(5)); - assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(new Integer(7)); + assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(5); + assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(7); } @Test @@ -124,8 +123,8 @@ public void testGenericMapElement() { gb.setShortMap(new HashMap<>()); BeanWrapper bw = new BeanWrapperImpl(gb); bw.setPropertyValue("shortMap[4]", "5"); - assertThat(bw.getPropertyValue("shortMap[4]")).isEqualTo(new Integer(5)); - assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(new Integer(5)); + assertThat(bw.getPropertyValue("shortMap[4]")).isEqualTo(5); + assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(5); } @Test @@ -136,8 +135,8 @@ public void testGenericMapWithKeyType() { input.put("4", "5"); input.put("6", "7"); bw.setPropertyValue("longMap", input); - assertThat(gb.getLongMap().get(new Long("4"))).isEqualTo("5"); - assertThat(gb.getLongMap().get(new Long("6"))).isEqualTo("7"); + assertThat(gb.getLongMap().get(4L)).isEqualTo("5"); + assertThat(gb.getLongMap().get(6L)).isEqualTo("7"); } @Test @@ -157,15 +156,15 @@ public void testGenericMapWithCollectionValue() { bw.registerCustomEditor(Number.class, new CustomNumberEditor(Integer.class, false)); Map> input = new HashMap<>(); HashSet value1 = new HashSet<>(); - value1.add(new Integer(1)); + value1.add(1); input.put("1", value1); ArrayList value2 = new ArrayList<>(); value2.add(Boolean.TRUE); input.put("2", value2); bw.setPropertyValue("collectionMap", input); - boolean condition1 = gb.getCollectionMap().get(new Integer(1)) instanceof HashSet; + boolean condition1 = gb.getCollectionMap().get(1) instanceof HashSet; assertThat(condition1).isTrue(); - boolean condition = gb.getCollectionMap().get(new Integer(2)) instanceof ArrayList; + boolean condition = gb.getCollectionMap().get(2) instanceof ArrayList; assertThat(condition).isTrue(); } @@ -176,9 +175,9 @@ public void testGenericMapElementWithCollectionValue() { BeanWrapper bw = new BeanWrapperImpl(gb); bw.registerCustomEditor(Number.class, new CustomNumberEditor(Integer.class, false)); HashSet value1 = new HashSet<>(); - value1.add(new Integer(1)); + value1.add(1); bw.setPropertyValue("collectionMap[1]", value1); - boolean condition = gb.getCollectionMap().get(new Integer(1)) instanceof HashSet; + boolean condition = gb.getCollectionMap().get(1) instanceof HashSet; assertThat(condition).isTrue(); } @@ -190,32 +189,32 @@ public void testGenericMapFromProperties() { input.setProperty("4", "5"); input.setProperty("6", "7"); bw.setPropertyValue("shortMap", input); - assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(new Integer(5)); - assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(new Integer(7)); + assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(5); + assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(7); } @Test public void testGenericListOfLists() throws MalformedURLException { GenericBean gb = new GenericBean<>(); - List> list = new LinkedList<>(); - list.add(new LinkedList<>()); + List> list = new ArrayList<>(); + list.add(new ArrayList<>()); gb.setListOfLists(list); BeanWrapper bw = new BeanWrapperImpl(gb); - bw.setPropertyValue("listOfLists[0][0]", new Integer(5)); - assertThat(bw.getPropertyValue("listOfLists[0][0]")).isEqualTo(new Integer(5)); - assertThat(gb.getListOfLists().get(0).get(0)).isEqualTo(new Integer(5)); + bw.setPropertyValue("listOfLists[0][0]", 5); + assertThat(bw.getPropertyValue("listOfLists[0][0]")).isEqualTo(5); + assertThat(gb.getListOfLists().get(0).get(0)).isEqualTo(5); } @Test public void testGenericListOfListsWithElementConversion() throws MalformedURLException { GenericBean gb = new GenericBean<>(); - List> list = new LinkedList<>(); - list.add(new LinkedList<>()); + List> list = new ArrayList<>(); + list.add(new ArrayList<>()); gb.setListOfLists(list); BeanWrapper bw = new BeanWrapperImpl(gb); bw.setPropertyValue("listOfLists[0][0]", "5"); - assertThat(bw.getPropertyValue("listOfLists[0][0]")).isEqualTo(new Integer(5)); - assertThat(gb.getListOfLists().get(0).get(0)).isEqualTo(new Integer(5)); + assertThat(bw.getPropertyValue("listOfLists[0][0]")).isEqualTo(5); + assertThat(gb.getListOfLists().get(0).get(0)).isEqualTo(5); } @Test @@ -246,7 +245,7 @@ public void testGenericListOfArraysWithElementConversion() throws MalformedURLEx @Test public void testGenericListOfMaps() throws MalformedURLException { GenericBean gb = new GenericBean<>(); - List> list = new LinkedList<>(); + List> list = new ArrayList<>(); list.add(new HashMap<>()); gb.setListOfMaps(list); BeanWrapper bw = new BeanWrapperImpl(gb); @@ -258,7 +257,7 @@ public void testGenericListOfMaps() throws MalformedURLException { @Test public void testGenericListOfMapsWithElementConversion() throws MalformedURLException { GenericBean gb = new GenericBean<>(); - List> list = new LinkedList<>(); + List> list = new ArrayList<>(); list.add(new HashMap<>()); gb.setListOfMaps(list); BeanWrapper bw = new BeanWrapperImpl(gb); @@ -295,24 +294,24 @@ public void testGenericMapOfMapsWithElementConversion() throws MalformedURLExcep public void testGenericMapOfLists() throws MalformedURLException { GenericBean gb = new GenericBean<>(); Map> map = new HashMap<>(); - map.put(new Integer(1), new LinkedList<>()); + map.put(1, new ArrayList<>()); gb.setMapOfLists(map); BeanWrapper bw = new BeanWrapperImpl(gb); - bw.setPropertyValue("mapOfLists[1][0]", new Integer(5)); - assertThat(bw.getPropertyValue("mapOfLists[1][0]")).isEqualTo(new Integer(5)); - assertThat(gb.getMapOfLists().get(new Integer(1)).get(0)).isEqualTo(new Integer(5)); + bw.setPropertyValue("mapOfLists[1][0]", 5); + assertThat(bw.getPropertyValue("mapOfLists[1][0]")).isEqualTo(5); + assertThat(gb.getMapOfLists().get(1).get(0)).isEqualTo(5); } @Test public void testGenericMapOfListsWithElementConversion() throws MalformedURLException { GenericBean gb = new GenericBean<>(); Map> map = new HashMap<>(); - map.put(new Integer(1), new LinkedList<>()); + map.put(1, new ArrayList<>()); gb.setMapOfLists(map); BeanWrapper bw = new BeanWrapperImpl(gb); bw.setPropertyValue("mapOfLists[1][0]", "5"); - assertThat(bw.getPropertyValue("mapOfLists[1][0]")).isEqualTo(new Integer(5)); - assertThat(gb.getMapOfLists().get(new Integer(1)).get(0)).isEqualTo(new Integer(5)); + assertThat(bw.getPropertyValue("mapOfLists[1][0]")).isEqualTo(5); + assertThat(gb.getMapOfLists().get(1).get(0)).isEqualTo(5); } @Test @@ -347,7 +346,7 @@ public void testGenericTypeNestingMapOfListOfInteger() throws Exception { @Test public void testGenericTypeNestingListOfMapOfInteger() throws Exception { - List> list = new LinkedList<>(); + List> list = new ArrayList<>(); Map map = new HashMap<>(); map.put("testKey", "5"); list.add(map); @@ -381,9 +380,9 @@ public void testGenericTypeNestingMapOfListOfListOfInteger() throws Exception { @Test public void testComplexGenericMap() { Map, List> inputMap = new HashMap<>(); - List inputKey = new LinkedList<>(); + List inputKey = new ArrayList<>(); inputKey.add("1"); - List inputValue = new LinkedList<>(); + List inputValue = new ArrayList<>(); inputValue.add("10"); inputMap.put(inputKey, inputValue); @@ -391,7 +390,7 @@ public void testComplexGenericMap() { BeanWrapper bw = new BeanWrapperImpl(holder); bw.setPropertyValue("genericMap", inputMap); - assertThat(holder.getGenericMap().keySet().iterator().next().get(0)).isEqualTo(new Integer(1)); + assertThat(holder.getGenericMap().keySet().iterator().next().get(0)).isEqualTo(1); assertThat(holder.getGenericMap().values().iterator().next().get(0)).isEqualTo(new Long(10)); } @@ -408,20 +407,20 @@ public void testComplexGenericMapWithCollectionConversion() { BeanWrapper bw = new BeanWrapperImpl(holder); bw.setPropertyValue("genericMap", inputMap); - assertThat(holder.getGenericMap().keySet().iterator().next().get(0)).isEqualTo(new Integer(1)); + assertThat(holder.getGenericMap().keySet().iterator().next().get(0)).isEqualTo(1); assertThat(holder.getGenericMap().values().iterator().next().get(0)).isEqualTo(new Long(10)); } @Test public void testComplexGenericIndexedMapEntry() { - List inputValue = new LinkedList<>(); + List inputValue = new ArrayList<>(); inputValue.add("10"); ComplexMapHolder holder = new ComplexMapHolder(); BeanWrapper bw = new BeanWrapperImpl(holder); bw.setPropertyValue("genericIndexedMap[1]", inputValue); - assertThat(holder.getGenericIndexedMap().keySet().iterator().next()).isEqualTo(new Integer(1)); + assertThat(holder.getGenericIndexedMap().keySet().iterator().next()).isEqualTo(1); assertThat(holder.getGenericIndexedMap().values().iterator().next().get(0)).isEqualTo(new Long(10)); } @@ -434,20 +433,20 @@ public void testComplexGenericIndexedMapEntryWithCollectionConversion() { BeanWrapper bw = new BeanWrapperImpl(holder); bw.setPropertyValue("genericIndexedMap[1]", inputValue); - assertThat(holder.getGenericIndexedMap().keySet().iterator().next()).isEqualTo(new Integer(1)); + assertThat(holder.getGenericIndexedMap().keySet().iterator().next()).isEqualTo(1); assertThat(holder.getGenericIndexedMap().values().iterator().next().get(0)).isEqualTo(new Long(10)); } @Test public void testComplexDerivedIndexedMapEntry() { - List inputValue = new LinkedList<>(); + List inputValue = new ArrayList<>(); inputValue.add("10"); ComplexMapHolder holder = new ComplexMapHolder(); BeanWrapper bw = new BeanWrapperImpl(holder); bw.setPropertyValue("derivedIndexedMap[1]", inputValue); - assertThat(holder.getDerivedIndexedMap().keySet().iterator().next()).isEqualTo(new Integer(1)); + assertThat(holder.getDerivedIndexedMap().keySet().iterator().next()).isEqualTo(1); assertThat(holder.getDerivedIndexedMap().values().iterator().next().get(0)).isEqualTo(new Long(10)); } @@ -460,7 +459,7 @@ public void testComplexDerivedIndexedMapEntryWithCollectionConversion() { BeanWrapper bw = new BeanWrapperImpl(holder); bw.setPropertyValue("derivedIndexedMap[1]", inputValue); - assertThat(holder.getDerivedIndexedMap().keySet().iterator().next()).isEqualTo(new Integer(1)); + assertThat(holder.getDerivedIndexedMap().keySet().iterator().next()).isEqualTo(1); assertThat(holder.getDerivedIndexedMap().values().iterator().next().get(0)).isEqualTo(new Long(10)); } @@ -470,9 +469,9 @@ public void testGenericallyTypedIntegerBean() throws Exception { BeanWrapper bw = new BeanWrapperImpl(gb); bw.setPropertyValue("genericProperty", "10"); bw.setPropertyValue("genericListProperty", new String[] {"20", "30"}); - assertThat(gb.getGenericProperty()).isEqualTo(new Integer(10)); - assertThat(gb.getGenericListProperty().get(0)).isEqualTo(new Integer(20)); - assertThat(gb.getGenericListProperty().get(1)).isEqualTo(new Integer(30)); + assertThat(gb.getGenericProperty()).isEqualTo(10); + assertThat(gb.getGenericListProperty().get(0)).isEqualTo(20); + assertThat(gb.getGenericListProperty().get(1)).isEqualTo(30); } @Test @@ -481,9 +480,9 @@ public void testGenericallyTypedSetOfIntegerBean() throws Exception { BeanWrapper bw = new BeanWrapperImpl(gb); bw.setPropertyValue("genericProperty", "10"); bw.setPropertyValue("genericListProperty", new String[] {"20", "30"}); - assertThat(gb.getGenericProperty().iterator().next()).isEqualTo(new Integer(10)); - assertThat(gb.getGenericListProperty().get(0).iterator().next()).isEqualTo(new Integer(20)); - assertThat(gb.getGenericListProperty().get(1).iterator().next()).isEqualTo(new Integer(30)); + assertThat(gb.getGenericProperty().iterator().next()).isEqualTo(10); + assertThat(gb.getGenericListProperty().get(0).iterator().next()).isEqualTo(20); + assertThat(gb.getGenericListProperty().get(1).iterator().next()).isEqualTo(30); } @Test diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java index a46b1957d994..0711189b5f59 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java @@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; diff --git a/spring-beans/src/test/java/org/springframework/beans/CachedIntrospectionResultsTests.java b/spring-beans/src/test/java/org/springframework/beans/CachedIntrospectionResultsTests.java index 4c867e3b69ef..cba7e4964a98 100644 --- a/spring-beans/src/test/java/org/springframework/beans/CachedIntrospectionResultsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/CachedIntrospectionResultsTests.java @@ -22,8 +22,8 @@ import org.junit.jupiter.api.Test; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.OverridingClassLoader; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; @@ -42,7 +42,7 @@ public void acceptAndClearClassLoader() throws Exception { assertThat(CachedIntrospectionResults.strongClassCache.containsKey(TestBean.class)).isTrue(); ClassLoader child = new OverridingClassLoader(getClass().getClassLoader()); - Class tbClass = child.loadClass("org.springframework.tests.sample.beans.TestBean"); + Class tbClass = child.loadClass("org.springframework.beans.testfixture.beans.TestBean"); assertThat(CachedIntrospectionResults.strongClassCache.containsKey(tbClass)).isFalse(); CachedIntrospectionResults.acceptClassLoader(child); bw = new BeanWrapperImpl(tbClass); diff --git a/spring-beans/src/test/java/org/springframework/beans/DirectFieldAccessorTests.java b/spring-beans/src/test/java/org/springframework/beans/DirectFieldAccessorTests.java index de10f1a982b6..1ca8ddc60298 100644 --- a/spring-beans/src/test/java/org/springframework/beans/DirectFieldAccessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/DirectFieldAccessorTests.java @@ -18,7 +18,7 @@ import org.junit.jupiter.api.Test; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java b/spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java index 640b1b920fb9..d9876729023f 100644 --- a/spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java @@ -25,7 +25,7 @@ import org.junit.jupiter.api.Test; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/BeanFactoryUtilsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/BeanFactoryUtilsTests.java index 47aa95f8a929..e869c9c6c906 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/BeanFactoryUtilsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/BeanFactoryUtilsTests.java @@ -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,6 +16,8 @@ package org.springframework.beans.factory; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -26,23 +28,25 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.StaticListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.beans.testfixture.beans.AnnotatedBean; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.IndexedTestBean; +import org.springframework.beans.testfixture.beans.TestAnnotation; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.beans.testfixture.beans.factory.DummyFactory; import org.springframework.cglib.proxy.NoOp; +import org.springframework.core.annotation.AliasFor; import org.springframework.core.io.Resource; -import org.springframework.tests.sample.beans.AnnotatedBean; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.IndexedTestBean; -import org.springframework.tests.sample.beans.TestAnnotation; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.tests.sample.beans.factory.DummyFactory; import org.springframework.util.ObjectUtils; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * @author Rod Johnson * @author Juergen Hoeller * @author Chris Beams + * @author Sam Brannen * @since 04.07.2003 */ public class BeanFactoryUtilsTests { @@ -59,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); @@ -89,7 +92,7 @@ public void testHierarchicalCountBeansWithNonHierarchicalFactory() { * 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 @@ -97,14 +100,14 @@ public void testHierarchicalCountBeansWithOverride() throws Exception { } @Test - public void testHierarchicalNamesWithNoMatch() throws Exception { + public void testHierarchicalNamesWithNoMatch() { List names = Arrays.asList( BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.listableBeanFactory, NoOp.class)); assertThat(names.size()).isEqualTo(0); } @Test - public void testHierarchicalNamesWithMatchOnlyInRoot() throws Exception { + public void testHierarchicalNamesWithMatchOnlyInRoot() { List names = Arrays.asList( BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.listableBeanFactory, IndexedTestBean.class)); assertThat(names.size()).isEqualTo(1); @@ -114,7 +117,7 @@ public void testHierarchicalNamesWithMatchOnlyInRoot() throws Exception { } @Test - public void testGetBeanNamesForTypeWithOverride() throws Exception { + public void testGetBeanNamesForTypeWithOverride() { List names = Arrays.asList( BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.listableBeanFactory, ITestBean.class)); // includes 2 TestBeans from FactoryBeans (DummyFactory definitions) @@ -232,7 +235,7 @@ public void testFindsBeansOfTypeWithDefaultFactory() { } @Test - public void testHierarchicalResolutionWithOverride() throws Exception { + public void testHierarchicalResolutionWithOverride() { Object test3 = this.listableBeanFactory.getBean("test3"); Object test = this.listableBeanFactory.getBean("test"); @@ -272,14 +275,14 @@ public void testHierarchicalResolutionWithOverride() throws Exception { } @Test - public void testHierarchicalNamesForAnnotationWithNoMatch() throws Exception { + public void testHierarchicalNamesForAnnotationWithNoMatch() { List names = Arrays.asList( BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.listableBeanFactory, Override.class)); assertThat(names.size()).isEqualTo(0); } @Test - public void testHierarchicalNamesForAnnotationWithMatchOnlyInRoot() throws Exception { + public void testHierarchicalNamesForAnnotationWithMatchOnlyInRoot() { List names = Arrays.asList( BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.listableBeanFactory, TestAnnotation.class)); assertThat(names.size()).isEqualTo(1); @@ -289,7 +292,7 @@ public void testHierarchicalNamesForAnnotationWithMatchOnlyInRoot() throws Excep } @Test - public void testGetBeanNamesForAnnotationWithOverride() throws Exception { + public void testGetBeanNamesForAnnotationWithOverride() { AnnotatedBean annotatedBean = new AnnotatedBean(); this.listableBeanFactory.registerSingleton("anotherAnnotatedBean", annotatedBean); List names = Arrays.asList( @@ -323,4 +326,168 @@ public void testIntDependencies() { assertThat(Arrays.equals(new String[] { "buffer" }, deps)).isTrue(); } + @Test + public void findAnnotationOnBean() { + this.listableBeanFactory.registerSingleton("controllerAdvice", new ControllerAdviceClass()); + this.listableBeanFactory.registerSingleton("restControllerAdvice", new RestControllerAdviceClass()); + testFindAnnotationOnBean(this.listableBeanFactory); + } + + @Test // gh-25520 + public void findAnnotationOnBeanWithStaticFactory() { + StaticListableBeanFactory lbf = new StaticListableBeanFactory(); + lbf.addBean("controllerAdvice", new ControllerAdviceClass()); + lbf.addBean("restControllerAdvice", new RestControllerAdviceClass()); + testFindAnnotationOnBean(lbf); + } + + private void testFindAnnotationOnBean(ListableBeanFactory lbf) { + assertControllerAdvice(lbf, "controllerAdvice"); + assertControllerAdvice(lbf, "restControllerAdvice"); + } + + private void assertControllerAdvice(ListableBeanFactory lbf, String beanName) { + ControllerAdvice controllerAdvice = lbf.findAnnotationOnBean(beanName, ControllerAdvice.class); + assertThat(controllerAdvice).isNotNull(); + assertThat(controllerAdvice.value()).isEqualTo("com.example"); + assertThat(controllerAdvice.basePackage()).isEqualTo("com.example"); + } + + @Test + public void isSingletonAndIsPrototypeWithStaticFactory() { + StaticListableBeanFactory lbf = new StaticListableBeanFactory(); + TestBean bean = new TestBean(); + DummyFactory fb1 = new DummyFactory(); + DummyFactory fb2 = new DummyFactory(); + fb2.setSingleton(false); + TestBeanSmartFactoryBean sfb1 = new TestBeanSmartFactoryBean(true, true); + TestBeanSmartFactoryBean sfb2 = new TestBeanSmartFactoryBean(true, false); + TestBeanSmartFactoryBean sfb3 = new TestBeanSmartFactoryBean(false, true); + TestBeanSmartFactoryBean sfb4 = new TestBeanSmartFactoryBean(false, false); + lbf.addBean("bean", bean); + lbf.addBean("fb1", fb1); + lbf.addBean("fb2", fb2); + lbf.addBean("sfb1", sfb1); + lbf.addBean("sfb2", sfb2); + lbf.addBean("sfb3", sfb3); + lbf.addBean("sfb4", sfb4); + + Map beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(lbf, ITestBean.class, true, true); + assertThat(beans.get("bean")).isSameAs(bean); + assertThat(beans.get("fb1")).isSameAs(fb1.getObject()); + assertThat(beans.get("fb2")).isInstanceOf(TestBean.class); + assertThat(beans.get("sfb1")).isInstanceOf(TestBean.class); + assertThat(beans.get("sfb2")).isInstanceOf(TestBean.class); + assertThat(beans.get("sfb3")).isInstanceOf(TestBean.class); + assertThat(beans.get("sfb4")).isInstanceOf(TestBean.class); + + assertThat(lbf.getBeanDefinitionCount()).isEqualTo(7); + assertThat(lbf.getBean("bean")).isInstanceOf(TestBean.class); + assertThat(lbf.getBean("&fb1")).isInstanceOf(FactoryBean.class); + assertThat(lbf.getBean("&fb2")).isInstanceOf(FactoryBean.class); + assertThat(lbf.getBean("&sfb1")).isInstanceOf(SmartFactoryBean.class); + assertThat(lbf.getBean("&sfb2")).isInstanceOf(SmartFactoryBean.class); + assertThat(lbf.getBean("&sfb3")).isInstanceOf(SmartFactoryBean.class); + assertThat(lbf.getBean("&sfb4")).isInstanceOf(SmartFactoryBean.class); + + assertThat(lbf.isSingleton("bean")).isTrue(); + assertThat(lbf.isSingleton("fb1")).isTrue(); + assertThat(lbf.isSingleton("fb2")).isTrue(); + assertThat(lbf.isSingleton("sfb1")).isTrue(); + assertThat(lbf.isSingleton("sfb2")).isTrue(); + assertThat(lbf.isSingleton("sfb3")).isTrue(); + assertThat(lbf.isSingleton("sfb4")).isTrue(); + + assertThat(lbf.isSingleton("&fb1")).isTrue(); + assertThat(lbf.isSingleton("&fb2")).isFalse(); + assertThat(lbf.isSingleton("&sfb1")).isTrue(); + assertThat(lbf.isSingleton("&sfb2")).isTrue(); + assertThat(lbf.isSingleton("&sfb3")).isFalse(); + assertThat(lbf.isSingleton("&sfb4")).isFalse(); + + assertThat(lbf.isPrototype("bean")).isFalse(); + assertThat(lbf.isPrototype("fb1")).isFalse(); + assertThat(lbf.isPrototype("fb2")).isFalse(); + assertThat(lbf.isPrototype("sfb1")).isFalse(); + assertThat(lbf.isPrototype("sfb2")).isFalse(); + assertThat(lbf.isPrototype("sfb3")).isFalse(); + assertThat(lbf.isPrototype("sfb4")).isFalse(); + + assertThat(lbf.isPrototype("&fb1")).isFalse(); + assertThat(lbf.isPrototype("&fb2")).isTrue(); + assertThat(lbf.isPrototype("&sfb1")).isTrue(); + assertThat(lbf.isPrototype("&sfb2")).isFalse(); + assertThat(lbf.isPrototype("&sfb3")).isTrue(); + assertThat(lbf.isPrototype("&sfb4")).isTrue(); + } + + + @Retention(RetentionPolicy.RUNTIME) + @interface ControllerAdvice { + + @AliasFor("basePackage") + String value() default ""; + + @AliasFor("value") + String basePackage() default ""; + } + + + @Retention(RetentionPolicy.RUNTIME) + @ControllerAdvice + @interface RestControllerAdvice { + + @AliasFor(annotation = ControllerAdvice.class) + String value() default ""; + + @AliasFor(annotation = ControllerAdvice.class) + String basePackage() default ""; + } + + + @ControllerAdvice("com.example") + static class ControllerAdviceClass { + } + + + @RestControllerAdvice("com.example") + static class RestControllerAdviceClass { + } + + + static class TestBeanSmartFactoryBean implements SmartFactoryBean { + + private final TestBean testBean = new TestBean("enigma", 42); + + private final boolean singleton; + + private final boolean prototype; + + TestBeanSmartFactoryBean(boolean singleton, boolean prototype) { + this.singleton = singleton; + this.prototype = prototype; + } + + @Override + public boolean isSingleton() { + return this.singleton; + } + + @Override + public boolean isPrototype() { + return this.prototype; + } + + @Override + public Class getObjectType() { + return TestBean.class; + } + + 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; + } + } + } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/ConcurrentBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/ConcurrentBeanFactoryTests.java deleted file mode 100644 index 87f7e0089a35..000000000000 --- a/spring-beans/src/test/java/org/springframework/beans/factory/ConcurrentBeanFactoryTests.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2002-2019 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.beans.factory; - -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.beans.propertyeditors.CustomDateEditor; -import org.springframework.tests.EnabledForTestGroups; -import org.springframework.tests.TestGroup; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; - -/** - * @author Guillaume Poirier - * @author Juergen Hoeller - * @author Chris Beams - * @since 10.03.2004 - */ -@EnabledForTestGroups(TestGroup.PERFORMANCE) -public class ConcurrentBeanFactoryTests { - - private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd"); - - private static final Date DATE_1, DATE_2; - - static { - try { - DATE_1 = DATE_FORMAT.parse("2004/08/08"); - DATE_2 = DATE_FORMAT.parse("2000/02/02"); - } - catch (ParseException e) { - throw new RuntimeException(e); - } - } - - - private static final Log logger = LogFactory.getLog(ConcurrentBeanFactoryTests.class); - - private BeanFactory factory; - - private final Set set = Collections.synchronizedSet(new HashSet<>()); - - private Throwable ex; - - - @BeforeEach - public void setup() throws Exception { - DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - new XmlBeanDefinitionReader(factory).loadBeanDefinitions( - qualifiedResource(ConcurrentBeanFactoryTests.class, "context.xml")); - - factory.addPropertyEditorRegistrar( - registry -> registry.registerCustomEditor(Date.class, - new CustomDateEditor((DateFormat) DATE_FORMAT.clone(), false))); - - this.factory = factory; - } - - - @Test - public void testSingleThread() { - for (int i = 0; i < 100; i++) { - performTest(); - } - } - - @Test - public void testConcurrent() { - for (int i = 0; i < 100; i++) { - TestRun run = new TestRun(); - run.setDaemon(true); - set.add(run); - } - for (Iterator it = new HashSet<>(set).iterator(); it.hasNext();) { - TestRun run = it.next(); - run.start(); - } - logger.info("Thread creation over, " + set.size() + " still active."); - synchronized (set) { - while (!set.isEmpty() && ex == null) { - try { - set.wait(); - } - catch (InterruptedException e) { - logger.info(e.toString()); - } - logger.info(set.size() + " threads still active."); - } - } - if (ex != null) { - throw new AssertionError("Unexpected exception", ex); - } - } - - private void performTest() { - ConcurrentBean b1 = (ConcurrentBean) factory.getBean("bean1"); - ConcurrentBean b2 = (ConcurrentBean) factory.getBean("bean2"); - - assertThat(b1.getDate()).isEqualTo(DATE_1); - assertThat(b2.getDate()).isEqualTo(DATE_2); - } - - - private class TestRun extends Thread { - - @Override - public void run() { - try { - for (int i = 0; i < 10000; i++) { - performTest(); - } - } - catch (Throwable e) { - ex = e; - } - finally { - synchronized (set) { - set.remove(this); - set.notifyAll(); - } - } - } - } - - - public static class ConcurrentBean { - - private Date date; - - public Date getDate() { - return date; - } - - public void setDate(Date date) { - this.date = date; - } - } - -} diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index b10dd72eba69..13321a7e9a2e 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -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. @@ -43,10 +43,7 @@ import javax.annotation.Priority; import javax.security.auth.Subject; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; @@ -62,9 +59,8 @@ import org.springframework.beans.factory.config.BeanExpressionContext; import org.springframework.beans.factory.config.BeanExpressionResolver; import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConstructorArgumentValues; -import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; +import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.config.TypedStringValue; @@ -75,10 +71,16 @@ import org.springframework.beans.factory.support.ChildBeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.ManagedList; -import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.ConstructorDependenciesBean; import org.springframework.beans.propertyeditors.CustomNumberEditor; +import org.springframework.beans.testfixture.beans.DependenciesBean; +import org.springframework.beans.testfixture.beans.DerivedTestBean; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.NestedTestBean; +import org.springframework.beans.testfixture.beans.SideEffectBean; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.beans.testfixture.beans.factory.DummyFactory; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationAwareOrderComparator; @@ -87,20 +89,9 @@ import org.springframework.core.convert.support.GenericConversionService; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; +import org.springframework.core.testfixture.io.SerializationTestUtils; +import org.springframework.core.testfixture.security.TestPrincipal; import org.springframework.lang.Nullable; -import org.springframework.tests.Assume; -import org.springframework.tests.EnabledForTestGroups; -import org.springframework.tests.TestGroup; -import org.springframework.tests.sample.beans.DependenciesBean; -import org.springframework.tests.sample.beans.DerivedTestBean; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.LifecycleBean; -import org.springframework.tests.sample.beans.NestedTestBean; -import org.springframework.tests.sample.beans.SideEffectBean; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.tests.sample.beans.factory.DummyFactory; -import org.springframework.util.SerializationTestUtils; -import org.springframework.util.StopWatch; import org.springframework.util.StringValueResolver; import static org.assertj.core.api.Assertions.assertThat; @@ -128,8 +119,6 @@ */ class DefaultListableBeanFactoryTests { - private static final Log factoryLog = LogFactory.getLog(DefaultListableBeanFactory.class); - private DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); @@ -139,7 +128,7 @@ void unreferencedSingletonWasInstantiated() { Properties p = new Properties(); p.setProperty("x1.(class)", KnowsIfInstantiated.class.getName()); assertThat(!KnowsIfInstantiated.wasInstantiated()).as("singleton not instantiated").isTrue(); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); lbf.preInstantiateSingletons(); assertThat(KnowsIfInstantiated.wasInstantiated()).as("singleton was instantiated").isTrue(); } @@ -151,7 +140,7 @@ void lazyInitialization() { p.setProperty("x1.(class)", KnowsIfInstantiated.class.getName()); p.setProperty("x1.(lazy-init)", "true"); assertThat(!KnowsIfInstantiated.wasInstantiated()).as("singleton not instantiated").isTrue(); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); assertThat(!KnowsIfInstantiated.wasInstantiated()).as("singleton not instantiated").isTrue(); lbf.preInstantiateSingletons(); @@ -168,7 +157,7 @@ void factoryBeanDidNotCreatePrototype() { DummyFactory.reset(); p.setProperty("x1.singleton", "false"); assertThat(!DummyFactory.wasPrototypeCreated()).as("prototype not instantiated").isTrue(); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); assertThat(!DummyFactory.wasPrototypeCreated()).as("prototype not instantiated").isTrue(); assertThat(lbf.getType("x1")).isEqualTo(TestBean.class); lbf.preInstantiateSingletons(); @@ -189,7 +178,7 @@ void prototypeFactoryBeanIgnoredByNonEagerTypeMatching() { DummyFactory.reset(); p.setProperty("x1.(singleton)", "false"); p.setProperty("x1.singleton", "false"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); assertThat(!DummyFactory.wasPrototypeCreated()).as("prototype not instantiated").isTrue(); String[] beanNames = lbf.getBeanNamesForType(TestBean.class, true, false); @@ -223,7 +212,7 @@ void singletonFactoryBeanIgnoredByNonEagerTypeMatching() { DummyFactory.reset(); p.setProperty("x1.(singleton)", "false"); p.setProperty("x1.singleton", "true"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); assertThat(!DummyFactory.wasPrototypeCreated()).as("prototype not instantiated").isTrue(); String[] beanNames = lbf.getBeanNamesForType(TestBean.class, true, false); @@ -256,7 +245,7 @@ void nonInitializedFactoryBeanIgnoredByNonEagerTypeMatching() { // Reset static state DummyFactory.reset(); p.setProperty("x1.singleton", "false"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); assertThat(!DummyFactory.wasPrototypeCreated()).as("prototype not instantiated").isTrue(); String[] beanNames = lbf.getBeanNamesForType(TestBean.class, true, false); @@ -289,7 +278,7 @@ void initializedFactoryBeanFoundByNonEagerTypeMatching() { // Reset static state DummyFactory.reset(); p.setProperty("x1.singleton", "false"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); lbf.preInstantiateSingletons(); assertThat(!DummyFactory.wasPrototypeCreated()).as("prototype not instantiated").isTrue(); @@ -367,7 +356,7 @@ void staticFactoryMethodFoundByNonEagerTypeMatching() { @Test void staticPrototypeFactoryMethodFoundByNonEagerTypeMatching() { RootBeanDefinition rbd = new RootBeanDefinition(TestBeanFactory.class); - rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE); rbd.setFactoryMethodName("createTestBean"); lbf.registerBeanDefinition("x1", rbd); @@ -423,7 +412,7 @@ void nonStaticPrototypeFactoryMethodFoundByNonEagerTypeMatching() { RootBeanDefinition rbd = new RootBeanDefinition(); rbd.setFactoryBeanName("factory"); rbd.setFactoryMethodName("createTestBeanNonStatic"); - rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE); lbf.registerBeanDefinition("x1", rbd); TestBeanFactory.initialized = false; @@ -483,7 +472,7 @@ void empty() { @Test void emptyPropertiesPopulation() { Properties p = new Properties(); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); assertThat(lbf.getBeanDefinitionCount() == 0).as("No beans defined after ignorable invalid").isTrue(); } @@ -492,7 +481,7 @@ void harmlessIgnorableRubbish() { Properties p = new Properties(); p.setProperty("foo", "bar"); p.setProperty("qwert", "er"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p, "test"); + registerBeanDefinitions(p, "test"); assertThat(lbf.getBeanDefinitionCount() == 0).as("No beans defined after harmless ignorable rubbish").isTrue(); } @@ -502,7 +491,7 @@ void propertiesPopulationWithNullPrefix() { p.setProperty("test.(class)", TestBean.class.getName()); p.setProperty("test.name", "Tony"); p.setProperty("test.age", "48"); - int count = (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + int count = registerBeanDefinitions(p); assertThat(count == 1).as("1 beans registered, not " + count).isTrue(); singleTestBean(lbf); } @@ -514,7 +503,7 @@ void propertiesPopulationWithPrefix() { p.setProperty(PREFIX + "test.(class)", TestBean.class.getName()); p.setProperty(PREFIX + "test.name", "Tony"); p.setProperty(PREFIX + "test.age", "0x30"); - int count = (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p, PREFIX); + int count = registerBeanDefinitions(p, PREFIX); assertThat(count == 1).as("1 beans registered, not " + count).isTrue(); singleTestBean(lbf); } @@ -532,7 +521,7 @@ void simpleReference() { p.setProperty(PREFIX + "kerry.age", "35"); p.setProperty(PREFIX + "kerry.spouse(ref)", "rod"); - int count = (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p, PREFIX); + int count = registerBeanDefinitions(p, PREFIX); assertThat(count == 2).as("2 beans registered, not " + count).isTrue(); TestBean kerry = lbf.getBean("kerry", TestBean.class); @@ -549,7 +538,7 @@ void propertiesWithDotsInKey() { p.setProperty("tb.(class)", TestBean.class.getName()); p.setProperty("tb.someMap[my.key]", "my.value"); - int count = (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + int count = registerBeanDefinitions(p); assertThat(count == 1).as("1 beans registered, not " + count).isTrue(); assertThat(lbf.getBeanDefinitionCount()).isEqualTo(1); @@ -567,7 +556,7 @@ void unresolvedReference() { p.setProperty(PREFIX + "kerry.age", "35"); p.setProperty(PREFIX + "kerry.spouse(ref)", "rod"); - new PropertiesBeanDefinitionReader(lbf).registerBeanDefinitions(p, PREFIX); + registerBeanDefinitions(p, PREFIX); assertThatExceptionOfType(BeansException.class).as("unresolved reference").isThrownBy(() -> lbf.getBean("kerry")); } @@ -690,7 +679,7 @@ void prototype() { Properties p = new Properties(); p.setProperty("kerry.(class)", TestBean.class.getName()); p.setProperty("kerry.age", "35"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); TestBean kerry1 = (TestBean) lbf.getBean("kerry"); TestBean kerry2 = (TestBean) lbf.getBean("kerry"); assertThat(kerry1 != null).as("Non null").isTrue(); @@ -701,7 +690,7 @@ void prototype() { p.setProperty("kerry.(class)", TestBean.class.getName()); p.setProperty("kerry.(scope)", "prototype"); p.setProperty("kerry.age", "35"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); kerry1 = (TestBean) lbf.getBean("kerry"); kerry2 = (TestBean) lbf.getBean("kerry"); assertThat(kerry1 != null).as("Non null").isTrue(); @@ -712,7 +701,7 @@ void prototype() { p.setProperty("kerry.(class)", TestBean.class.getName()); p.setProperty("kerry.(scope)", "singleton"); p.setProperty("kerry.age", "35"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); kerry1 = (TestBean) lbf.getBean("kerry"); kerry2 = (TestBean) lbf.getBean("kerry"); assertThat(kerry1 != null).as("Non null").isTrue(); @@ -731,7 +720,7 @@ void prototypeCircleLeadsToException() { p.setProperty("rod.age", "34"); p.setProperty("rod.spouse", "*kerry"); - new PropertiesBeanDefinitionReader(lbf).registerBeanDefinitions(p); + registerBeanDefinitions(p); assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> lbf.getBean("kerry")) .satisfies(ex -> assertThat(ex.contains(BeanCurrentlyInCreationException.class)).isTrue()); @@ -745,7 +734,7 @@ void prototypeExtendsPrototype() { p.setProperty("kerry.(parent)", "wife"); p.setProperty("kerry.age", "35"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); TestBean kerry1 = (TestBean) lbf.getBean("kerry"); TestBean kerry2 = (TestBean) lbf.getBean("kerry"); assertThat(kerry1.getName()).isEqualTo("kerry"); @@ -760,7 +749,7 @@ void prototypeExtendsPrototype() { p.setProperty("kerry.(parent)", "wife"); p.setProperty("kerry.(singleton)", "false"); p.setProperty("kerry.age", "35"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); assertThat(lbf.isSingleton("kerry")).isFalse(); kerry1 = (TestBean) lbf.getBean("kerry"); kerry2 = (TestBean) lbf.getBean("kerry"); @@ -772,7 +761,7 @@ void prototypeExtendsPrototype() { p.setProperty("kerry.(class)", TestBean.class.getName()); p.setProperty("kerry.(singleton)", "true"); p.setProperty("kerry.age", "35"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); kerry1 = (TestBean) lbf.getBean("kerry"); kerry2 = (TestBean) lbf.getBean("kerry"); assertThat(kerry1 != null).as("Non null").isTrue(); @@ -796,12 +785,13 @@ void canReferenceParentBeanFromChildViaAlias() { 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 @@ -823,9 +813,9 @@ void nameAlreadyBound() { Properties p = new Properties(); p.setProperty("kerry.(class)", TestBean.class.getName()); p.setProperty("kerry.age", "35"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); try { - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); } catch (BeanDefinitionStoreException ex) { assertThat(ex.getBeanName()).isEqualTo("kerry"); @@ -965,7 +955,7 @@ void beanReferenceWithNewSyntax() { p.setProperty("k.(class)", TestBean.class.getName()); p.setProperty("k.name", "kerry"); p.setProperty("k.spouse", "*r"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); TestBean k = (TestBean) lbf.getBean("k"); TestBean r = (TestBean) lbf.getBean("r"); assertThat(k.getSpouse() == r).isTrue(); @@ -977,7 +967,7 @@ void canEscapeBeanReferenceSyntax() { Properties p = new Properties(); p.setProperty("r.(class)", TestBean.class.getName()); p.setProperty("r.name", "*" + name); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); TestBean r = (TestBean) lbf.getBean("r"); assertThat(r.getName().equals(name)).isTrue(); } @@ -1081,7 +1071,7 @@ void registerExistingSingletonWithReference() { p.setProperty("test.name", "Tony"); p.setProperty("test.age", "48"); p.setProperty("test.spouse(ref)", "singletonObject"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); Object singletonObject = new TestBean(); lbf.registerSingleton("singletonObject", singletonObject); @@ -1117,7 +1107,7 @@ void registerExistingSingletonWithNameOverriding() { p.setProperty("test.name", "Tony"); p.setProperty("test.age", "48"); p.setProperty("test.spouse(ref)", "singletonObject"); - (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); + registerBeanDefinitions(p); lbf.registerBeanDefinition("singletonObject", new RootBeanDefinition(PropertiesFactoryBean.class)); Object singletonObject = new TestBean(); lbf.registerSingleton("singletonObject", singletonObject); @@ -1181,11 +1171,11 @@ void registerExistingSingletonWithAlreadyBound() { @Test void reregisterBeanDefinition() { RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class); - bd1.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd1.setScope(BeanDefinition.SCOPE_PROTOTYPE); lbf.registerBeanDefinition("testBean", bd1); assertThat(lbf.getBean("testBean")).isInstanceOf(TestBean.class); RootBeanDefinition bd2 = new RootBeanDefinition(NestedTestBean.class); - bd2.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd2.setScope(BeanDefinition.SCOPE_PROTOTYPE); lbf.registerBeanDefinition("testBean", bd2); assertThat(lbf.getBean("testBean")).isInstanceOf(NestedTestBean.class); } @@ -1216,16 +1206,16 @@ void arrayPropertyWithOptionalAutowiring() throws MalformedURLException { @Test void arrayConstructorWithAutowiring() { - lbf.registerSingleton("integer1", new Integer(4)); - lbf.registerSingleton("integer2", new Integer(5)); + lbf.registerSingleton("integer1",4); + lbf.registerSingleton("integer2", 5); RootBeanDefinition rbd = new RootBeanDefinition(ArrayBean.class); rbd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); lbf.registerBeanDefinition("arrayBean", rbd); ArrayBean ab = (ArrayBean) lbf.getBean("arrayBean"); - assertThat(ab.getIntegerArray()[0]).isEqualTo(new Integer(4)); - assertThat(ab.getIntegerArray()[1]).isEqualTo(new Integer(5)); + assertThat(ab.getIntegerArray()[0]).isEqualTo(4); + assertThat(ab.getIntegerArray()[1]).isEqualTo(5); } @Test @@ -1240,8 +1230,8 @@ void arrayConstructorWithOptionalAutowiring() { @Test void doubleArrayConstructorWithAutowiring() throws MalformedURLException { - lbf.registerSingleton("integer1", new Integer(4)); - lbf.registerSingleton("integer2", new Integer(5)); + lbf.registerSingleton("integer1", 4); + lbf.registerSingleton("integer2", 5); lbf.registerSingleton("resource1", new UrlResource("http://localhost:8080")); lbf.registerSingleton("resource2", new UrlResource("http://localhost:9090")); @@ -1250,8 +1240,8 @@ void doubleArrayConstructorWithAutowiring() throws MalformedURLException { lbf.registerBeanDefinition("arrayBean", rbd); ArrayBean ab = (ArrayBean) lbf.getBean("arrayBean"); - assertThat(ab.getIntegerArray()[0]).isEqualTo(new Integer(4)); - assertThat(ab.getIntegerArray()[1]).isEqualTo(new Integer(5)); + assertThat(ab.getIntegerArray()[0]).isEqualTo(4); + assertThat(ab.getIntegerArray()[1]).isEqualTo(5); assertThat(ab.getResourceArray()[0]).isEqualTo(new UrlResource("http://localhost:8080")); assertThat(ab.getResourceArray()[1]).isEqualTo(new UrlResource("http://localhost:9090")); } @@ -1430,6 +1420,29 @@ void getBeanByTypeWithNoneFound() { lbf.getBean(TestBean.class)); } + @Test + void getBeanByTypeWithLateRegistration() { + DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); + assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() -> + lbf.getBean(TestBean.class)); + RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class); + lbf.registerBeanDefinition("bd1", bd1); + TestBean bean = lbf.getBean(TestBean.class); + assertThat(bean.getBeanName()).isEqualTo("bd1"); + } + + @Test + void getBeanByTypeWithLateRegistrationAgainstFrozen() { + DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); + lbf.freezeConfiguration(); + assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() -> + lbf.getBean(TestBean.class)); + RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class); + lbf.registerBeanDefinition("bd1", bd1); + TestBean bean = lbf.getBean(TestBean.class); + assertThat(bean.getBeanName()).isEqualTo("bd1"); + } + @Test void getBeanByTypeDefinedInParent() { DefaultListableBeanFactory parent = new DefaultListableBeanFactory(); @@ -1642,7 +1655,7 @@ void getBeanByTypeInstanceDefinedInParent() { void getBeanByTypeInstanceWithAmbiguity() { RootBeanDefinition bd1 = createConstructorDependencyBeanDefinition(99); RootBeanDefinition bd2 = new RootBeanDefinition(ConstructorDependency.class); - bd2.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd2.setScope(BeanDefinition.SCOPE_PROTOTYPE); bd2.getConstructorArgumentValues().addGenericArgumentValue("43"); lbf.registerBeanDefinition("bd1", bd1); lbf.registerBeanDefinition("bd2", bd2); @@ -1765,7 +1778,7 @@ void beanProviderSerialization() throws Exception { lbf.setSerializationId("test"); ObjectProvider provider = lbf.getBeanProvider(ConstructorDependency.class); - ObjectProvider deserialized = (ObjectProvider) SerializationTestUtils.serializeAndDeserialize(provider); + ObjectProvider deserialized = SerializationTestUtils.serializeAndDeserialize(provider); assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy( deserialized::getObject); assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() -> @@ -1777,10 +1790,10 @@ void beanProviderSerialization() throws Exception { @Test void getBeanWithArgsNotCreatedForFactoryBeanChecking() { RootBeanDefinition bd1 = new RootBeanDefinition(ConstructorDependency.class); - bd1.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd1.setScope(BeanDefinition.SCOPE_PROTOTYPE); lbf.registerBeanDefinition("bd1", bd1); RootBeanDefinition bd2 = new RootBeanDefinition(ConstructorDependencyFactoryBean.class); - bd2.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd2.setScope(BeanDefinition.SCOPE_PROTOTYPE); lbf.registerBeanDefinition("bd2", bd2); ConstructorDependency bean = lbf.getBean(ConstructorDependency.class, 42); @@ -1797,7 +1810,7 @@ void getBeanWithArgsNotCreatedForFactoryBeanChecking() { private RootBeanDefinition createConstructorDependencyBeanDefinition(int age) { RootBeanDefinition bd = new RootBeanDefinition(ConstructorDependency.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bd.getConstructorArgumentValues().addGenericArgumentValue(age); return bd; } @@ -1826,8 +1839,7 @@ void autowireBeanWithFactoryBeanByType() { 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 @@ -2283,7 +2295,7 @@ void prototypeFactoryBeanNotEagerlyCalledInCaseOfBeanClassName() { @Test void prototypeStringCreatedRepeatedly() { RootBeanDefinition stringDef = new RootBeanDefinition(String.class); - stringDef.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + stringDef.setScope(BeanDefinition.SCOPE_PROTOTYPE); stringDef.getConstructorArgumentValues().addGenericArgumentValue(new TypedStringValue("value")); lbf.registerBeanDefinition("string", stringDef); String val1 = lbf.getBean("string", String.class); @@ -2299,7 +2311,7 @@ void prototypeWithArrayConversionForConstructor() { list.add("myName"); list.add("myBeanName"); RootBeanDefinition bd = new RootBeanDefinition(DerivedTestBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bd.getConstructorArgumentValues().addGenericArgumentValue(list); lbf.registerBeanDefinition("test", bd); DerivedTestBean tb = (DerivedTestBean) lbf.getBean("test"); @@ -2317,7 +2329,7 @@ void prototypeWithArrayConversionForFactoryMethod() { list.add("myName"); list.add("myBeanName"); RootBeanDefinition bd = new RootBeanDefinition(DerivedTestBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bd.setFactoryMethodName("create"); bd.getConstructorArgumentValues().addGenericArgumentValue(list); lbf.registerBeanDefinition("test", bd); @@ -2330,163 +2342,6 @@ void prototypeWithArrayConversionForFactoryMethod() { assertThat(tb2.getBeanName()).isEqualTo("myBeanName"); } - @Test - @EnabledForTestGroups(TestGroup.PERFORMANCE) - void prototypeCreationIsFastEnough() { - Assume.notLogging(factoryLog); - RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); - rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); - lbf.registerBeanDefinition("test", rbd); - lbf.freezeConfiguration(); - StopWatch sw = new StopWatch(); - sw.start("prototype"); - for (int i = 0; i < 100000; i++) { - lbf.getBean("test"); - } - sw.stop(); - // System.out.println(sw.getTotalTimeMillis()); - assertThat(sw.getTotalTimeMillis() < 3000).as("Prototype creation took too long: " + sw.getTotalTimeMillis()).isTrue(); - } - - @Test - @EnabledForTestGroups(TestGroup.PERFORMANCE) - void prototypeCreationWithDependencyCheckIsFastEnough() { - Assume.notLogging(factoryLog); - RootBeanDefinition rbd = new RootBeanDefinition(LifecycleBean.class); - rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); - rbd.setDependencyCheck(RootBeanDefinition.DEPENDENCY_CHECK_OBJECTS); - lbf.registerBeanDefinition("test", rbd); - lbf.addBeanPostProcessor(new LifecycleBean.PostProcessor()); - lbf.freezeConfiguration(); - StopWatch sw = new StopWatch(); - sw.start("prototype"); - for (int i = 0; i < 100000; i++) { - lbf.getBean("test"); - } - sw.stop(); - // System.out.println(sw.getTotalTimeMillis()); - assertThat(sw.getTotalTimeMillis() < 3000).as("Prototype creation took too long: " + sw.getTotalTimeMillis()).isTrue(); - } - - @Test - @EnabledForTestGroups(TestGroup.PERFORMANCE) - void prototypeCreationWithConstructorArgumentsIsFastEnough() { - Assume.notLogging(factoryLog); - RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); - rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); - rbd.getConstructorArgumentValues().addGenericArgumentValue("juergen"); - rbd.getConstructorArgumentValues().addGenericArgumentValue("99"); - lbf.registerBeanDefinition("test", rbd); - lbf.freezeConfiguration(); - StopWatch sw = new StopWatch(); - sw.start("prototype"); - for (int i = 0; i < 100000; i++) { - TestBean tb = (TestBean) lbf.getBean("test"); - assertThat(tb.getName()).isEqualTo("juergen"); - assertThat(tb.getAge()).isEqualTo(99); - } - sw.stop(); - assertThat(sw.getTotalTimeMillis() < 3000).as("Prototype creation took too long: " + sw.getTotalTimeMillis()).isTrue(); - } - - @Test - @EnabledForTestGroups(TestGroup.PERFORMANCE) - void prototypeCreationWithResolvedConstructorArgumentsIsFastEnough() { - Assume.notLogging(factoryLog); - RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); - rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); - rbd.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference("spouse")); - lbf.registerBeanDefinition("test", rbd); - lbf.registerBeanDefinition("spouse", new RootBeanDefinition(TestBean.class)); - lbf.freezeConfiguration(); - TestBean spouse = (TestBean) lbf.getBean("spouse"); - StopWatch sw = new StopWatch(); - sw.start("prototype"); - for (int i = 0; i < 100000; i++) { - TestBean tb = (TestBean) lbf.getBean("test"); - assertThat(tb.getSpouse()).isSameAs(spouse); - } - sw.stop(); - // System.out.println(sw.getTotalTimeMillis()); - assertThat(sw.getTotalTimeMillis() < 4000).as("Prototype creation took too long: " + sw.getTotalTimeMillis()).isTrue(); - } - - @Test - @EnabledForTestGroups(TestGroup.PERFORMANCE) - void prototypeCreationWithPropertiesIsFastEnough() { - Assume.notLogging(factoryLog); - RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); - rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); - rbd.getPropertyValues().add("name", "juergen"); - rbd.getPropertyValues().add("age", "99"); - lbf.registerBeanDefinition("test", rbd); - lbf.freezeConfiguration(); - StopWatch sw = new StopWatch(); - sw.start("prototype"); - for (int i = 0; i < 100000; i++) { - TestBean tb = (TestBean) lbf.getBean("test"); - assertThat(tb.getName()).isEqualTo("juergen"); - assertThat(tb.getAge()).isEqualTo(99); - } - sw.stop(); - // System.out.println(sw.getTotalTimeMillis()); - assertThat(sw.getTotalTimeMillis() < 4000).as("Prototype creation took too long: " + sw.getTotalTimeMillis()).isTrue(); - } - - @Test - @EnabledForTestGroups(TestGroup.PERFORMANCE) - void prototypeCreationWithResolvedPropertiesIsFastEnough() { - Assume.notLogging(factoryLog); - RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); - rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); - rbd.getPropertyValues().add("spouse", new RuntimeBeanReference("spouse")); - lbf.registerBeanDefinition("test", rbd); - lbf.registerBeanDefinition("spouse", new RootBeanDefinition(TestBean.class)); - lbf.freezeConfiguration(); - TestBean spouse = (TestBean) lbf.getBean("spouse"); - StopWatch sw = new StopWatch(); - sw.start("prototype"); - for (int i = 0; i < 100000; i++) { - TestBean tb = (TestBean) lbf.getBean("test"); - assertThat(tb.getSpouse()).isSameAs(spouse); - } - sw.stop(); - // System.out.println(sw.getTotalTimeMillis()); - assertThat(sw.getTotalTimeMillis() < 4000).as("Prototype creation took too long: " + sw.getTotalTimeMillis()).isTrue(); - } - - @Test - @EnabledForTestGroups(TestGroup.PERFORMANCE) - void singletonLookupByNameIsFastEnough() { - Assume.notLogging(factoryLog); - lbf.registerBeanDefinition("test", new RootBeanDefinition(TestBean.class)); - lbf.freezeConfiguration(); - StopWatch sw = new StopWatch(); - sw.start("singleton"); - for (int i = 0; i < 1000000; i++) { - lbf.getBean("test"); - } - sw.stop(); - // System.out.println(sw.getTotalTimeMillis()); - assertThat(sw.getTotalTimeMillis() < 1000).as("Singleton lookup took too long: " + sw.getTotalTimeMillis()).isTrue(); - } - - @Test - @EnabledForTestGroups(TestGroup.PERFORMANCE) - void singletonLookupByTypeIsFastEnough() { - Assume.notLogging(factoryLog); - lbf.registerBeanDefinition("test", new RootBeanDefinition(TestBean.class)); - lbf.freezeConfiguration(); - StopWatch sw = new StopWatch(); - sw.start("singleton"); - for (int i = 0; i < 1000000; i++) { - lbf.getBean(TestBean.class); - } - sw.stop(); - // System.out.println(sw.getTotalTimeMillis()); - assertThat(sw.getTotalTimeMillis() < 1000).as("Singleton lookup took too long: " + sw.getTotalTimeMillis()).isTrue(); - } - @Test void beanPostProcessorWithWrappedObjectAndDisposableBean() { RootBeanDefinition bd = new RootBeanDefinition(BeanWithDisposableBean.class); @@ -2533,8 +2388,7 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) { 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 @@ -2548,14 +2402,13 @@ void destroyMethodOnInnerBean() { 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 void destroyMethodOnInnerBeanAsPrototype() { RootBeanDefinition innerBd = new RootBeanDefinition(BeanWithDestroyMethod.class); - innerBd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + innerBd.setScope(BeanDefinition.SCOPE_PROTOTYPE); innerBd.setDestroyMethodName("close"); RootBeanDefinition bd = new RootBeanDefinition(BeanWithDestroyMethod.class); bd.setDestroyMethodName("close"); @@ -2564,8 +2417,7 @@ void destroyMethodOnInnerBeanAsPrototype() { 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 @@ -2599,7 +2451,7 @@ private void findTypeOfPrototypeFactoryMethodOnBeanInstance(boolean singleton) { factoryMethodDefinitionWithProperties.setFactoryBeanName("factoryBeanInstance"); factoryMethodDefinitionWithProperties.setFactoryMethodName("create"); if (!singleton) { - factoryMethodDefinitionWithProperties.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + factoryMethodDefinitionWithProperties.setScope(BeanDefinition.SCOPE_PROTOTYPE); } lbf.registerBeanDefinition("fmWithProperties", factoryMethodDefinitionWithProperties); @@ -2607,7 +2459,7 @@ private void findTypeOfPrototypeFactoryMethodOnBeanInstance(boolean singleton) { factoryMethodDefinitionGeneric.setFactoryBeanName("factoryBeanInstance"); factoryMethodDefinitionGeneric.setFactoryMethodName("createGeneric"); if (!singleton) { - factoryMethodDefinitionGeneric.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + factoryMethodDefinitionGeneric.setScope(BeanDefinition.SCOPE_PROTOTYPE); } lbf.registerBeanDefinition("fmGeneric", factoryMethodDefinitionGeneric); @@ -2618,7 +2470,7 @@ private void findTypeOfPrototypeFactoryMethodOnBeanInstance(boolean singleton) { cvals.addGenericArgumentValue(expectedNameFromArgs); factoryMethodDefinitionWithArgs.setConstructorArgumentValues(cvals); if (!singleton) { - factoryMethodDefinitionWithArgs.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + factoryMethodDefinitionWithArgs.setScope(BeanDefinition.SCOPE_PROTOTYPE); } lbf.registerBeanDefinition("fmWithArgs", factoryMethodDefinitionWithArgs); @@ -2676,7 +2528,7 @@ void explicitScopeInheritanceForChildBeanDefinitions() { String theChildScope = "bonanza!"; RootBeanDefinition parent = new RootBeanDefinition(); - parent.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + parent.setScope(BeanDefinition.SCOPE_PROTOTYPE); AbstractBeanDefinition child = BeanDefinitionBuilder.childBeanDefinition("parent").getBeanDefinition(); child.setBeanClass(TestBean.class); @@ -2687,14 +2539,15 @@ void explicitScopeInheritanceForChildBeanDefinitions() { 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); @@ -2704,8 +2557,7 @@ void scopeInheritanceForChildBeanDefinitions() { 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 @@ -2721,10 +2573,10 @@ void fieldSettingWithInstantiationAwarePostProcessorWithShortCircuit() { private void doTestFieldSettingWithInstantiationAwarePostProcessor(final boolean skipPropertyPopulation) { RootBeanDefinition bd = new RootBeanDefinition(TestBean.class); int ageSetByPropertyValue = 27; - bd.getPropertyValues().addPropertyValue(new PropertyValue("age", new Integer(ageSetByPropertyValue))); + bd.getPropertyValues().addPropertyValue(new PropertyValue("age", ageSetByPropertyValue)); lbf.registerBeanDefinition("test", bd); final String nameSetOnField = "nameSetOnField"; - lbf.addBeanPostProcessor(new InstantiationAwareBeanPostProcessorAdapter() { + lbf.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() { @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { TestBean tb = (TestBean) bean; @@ -2741,15 +2593,12 @@ public boolean postProcessAfterInstantiation(Object bean, String beanName) throw }); 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); } } @@ -2757,7 +2606,7 @@ public boolean postProcessAfterInstantiation(Object bean, String beanName) throw @SuppressWarnings({ "unchecked", "rawtypes" }) void initSecurityAwarePrototypeBean() { RootBeanDefinition bd = new RootBeanDefinition(TestSecuredBean.class); - bd.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bd.setInitMethodName("init"); lbf.registerBeanDefinition("test", bd); final Subject subject = new Subject(); @@ -2822,62 +2671,18 @@ void nonPublicEnum() { assertThat(holder.getNonPublicEnum()).isEqualTo(NonPublicEnum.VALUE_1); } - /** - * Test that by-type bean lookup caching is working effectively by searching for a - * bean of type B 10K times within a container having 1K additional beans of type A. - * Prior to by-type caching, each bean lookup would traverse the entire container - * (all 1001 beans), performing expensive assignability checks, etc. Now these - * operations are necessary only once, providing a dramatic performance improvement. - * On load-free modern hardware (e.g. an 8-core MPB), this method should complete well - * under the 1000 ms timeout, usually ~= 300ms. With caching removed and on the same - * hardware the method will take ~13000 ms. See SPR-6870. - */ - @Test - @Timeout(1) - @EnabledForTestGroups(TestGroup.PERFORMANCE) - void byTypeLookupIsFastEnough() { - for (int i = 0; i < 1000; i++) { - lbf.registerBeanDefinition("a" + i, new RootBeanDefinition(A.class)); - } - lbf.registerBeanDefinition("b", new RootBeanDefinition(B.class)); - - lbf.freezeConfiguration(); - - for (int i = 0; i < 10000; i++) { - lbf.getBean(B.class); - } - } - - @Test - @Timeout(1) - @EnabledForTestGroups(TestGroup.PERFORMANCE) - void registrationOfManyBeanDefinitionsIsFastEnough() { - lbf.registerBeanDefinition("b", new RootBeanDefinition(B.class)); - // lbf.getBean("b"); - for (int i = 0; i < 100000; i++) { - lbf.registerBeanDefinition("a" + i, new RootBeanDefinition(A.class)); - } + @SuppressWarnings("deprecation") + private int registerBeanDefinitions(Properties p) { + return (new org.springframework.beans.factory.support.PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p); } - @Test - @Timeout(1) - @EnabledForTestGroups(TestGroup.PERFORMANCE) - void registrationOfManySingletonsIsFastEnough() { - lbf.registerBeanDefinition("b", new RootBeanDefinition(B.class)); - // lbf.getBean("b"); - - for (int i = 0; i < 100000; i++) { - lbf.registerSingleton("a" + i, new A()); - } + @SuppressWarnings("deprecation") + private int registerBeanDefinitions(Properties p, String prefix) { + return (new org.springframework.beans.factory.support.PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p, prefix); } - static class A { } - - static class B { } - - public static class NoDependencies { private NoDependencies() { @@ -3232,7 +3037,7 @@ public Object convertIfNecessary(Object value, @Nullable Class requiredType) { } } else if (value instanceof String && int.class.isAssignableFrom(requiredType)) { - return new Integer(5); + return 5; } else { return value; @@ -3253,38 +3058,6 @@ public Object convertIfNecessary(Object value, @Nullable Class requiredType, @Nu } - private static class TestPrincipal implements Principal { - - private String name; - - public TestPrincipal(String name) { - this.name = name; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof TestPrincipal)) { - return false; - } - TestPrincipal p = (TestPrincipal) obj; - return this.name.equals(p.name); - } - - @Override - public int hashCode() { - return this.name.hashCode(); - } - } - - @SuppressWarnings("unused") private static class TestSecuredBean { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java index 3bffa8ca1fae..9a6f86a8382c 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java @@ -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. @@ -27,11 +27,11 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.core.io.Resource; -import org.springframework.stereotype.Component; +import org.springframework.core.testfixture.stereotype.Component; import org.springframework.util.Assert; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * @author Rob Harrop @@ -286,7 +286,7 @@ public Object postProcessAfterInitialization(Object bean, String beanName) { } AtomicInteger c = count.get(beanName); if (c == null) { - c = new AtomicInteger(0); + c = new AtomicInteger(); count.put(beanName, c); } c.incrementAndGet(); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java index ce0add1cee8a..c746f21949e8 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,13 +24,13 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; @@ -64,16 +64,16 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.IndexedTestBean; +import org.springframework.beans.testfixture.beans.NestedTestBean; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.Order; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.IndexedTestBean; -import org.springframework.tests.sample.beans.NestedTestBean; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.core.testfixture.io.SerializationTestUtils; import org.springframework.util.ReflectionUtils; -import org.springframework.util.SerializationTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -120,7 +120,7 @@ public void testIncompleteBeanDefinition() { @Test public void testResourceInjection() { RootBeanDefinition bd = new RootBeanDefinition(ResourceInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -137,7 +137,7 @@ public void testResourceInjection() { @Test public void testExtendedResourceInjection() { RootBeanDefinition bd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -255,7 +255,7 @@ public void testExtendedResourceInjectionWithDefaultMethod() { public void testExtendedResourceInjectionWithAtRequired() { bf.addBeanPostProcessor(new RequiredAnnotationBeanPostProcessor()); RootBeanDefinition bd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -297,10 +297,125 @@ public void testOptionalResourceInjection() { assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2); } + @Test + public void testOptionalResourceInjectionWithSingletonRemoval() { + RootBeanDefinition rbd = new RootBeanDefinition(OptionalResourceInjectionBean.class); + rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE); + bf.registerBeanDefinition("annotatedBean", rbd); + TestBean tb = new TestBean(); + bf.registerSingleton("testBean", tb); + IndexedTestBean itb = new IndexedTestBean(); + bf.registerSingleton("indexedTestBean", itb); + NestedTestBean ntb1 = new NestedTestBean(); + bf.registerSingleton("nestedTestBean1", ntb1); + NestedTestBean ntb2 = new NestedTestBean(); + bf.registerSingleton("nestedTestBean2", ntb2); + + OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean"); + assertThat(bean.getTestBean()).isSameAs(tb); + assertThat(bean.getTestBean2()).isSameAs(tb); + assertThat(bean.getTestBean3()).isSameAs(tb); + assertThat(bean.getTestBean4()).isSameAs(tb); + assertThat(bean.getIndexedTestBean()).isSameAs(itb); + assertThat(bean.getNestedTestBeans().length).isEqualTo(2); + assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1); + assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2); + assertThat(bean.nestedTestBeansField.length).isEqualTo(2); + assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1); + assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2); + + bf.destroySingleton("testBean"); + + bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean"); + assertThat(bean.getTestBean()).isNull(); + assertThat(bean.getTestBean2()).isNull(); + assertThat(bean.getTestBean3()).isNull(); + assertThat(bean.getTestBean4()).isNull(); + assertThat(bean.getIndexedTestBean()).isSameAs(itb); + assertThat(bean.getNestedTestBeans().length).isEqualTo(2); + assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1); + assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2); + assertThat(bean.nestedTestBeansField.length).isEqualTo(2); + assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1); + assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2); + + bf.registerSingleton("testBean", tb); + + bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean"); + assertThat(bean.getTestBean()).isSameAs(tb); + assertThat(bean.getTestBean2()).isSameAs(tb); + assertThat(bean.getTestBean3()).isSameAs(tb); + assertThat(bean.getTestBean4()).isSameAs(tb); + assertThat(bean.getIndexedTestBean()).isSameAs(itb); + assertThat(bean.getNestedTestBeans().length).isEqualTo(2); + assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1); + assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2); + assertThat(bean.nestedTestBeansField.length).isEqualTo(2); + assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1); + assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2); + } + + @Test + public void testOptionalResourceInjectionWithBeanDefinitionRemoval() { + RootBeanDefinition rbd = new RootBeanDefinition(OptionalResourceInjectionBean.class); + rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE); + bf.registerBeanDefinition("annotatedBean", rbd); + bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class)); + IndexedTestBean itb = new IndexedTestBean(); + bf.registerSingleton("indexedTestBean", itb); + NestedTestBean ntb1 = new NestedTestBean(); + bf.registerSingleton("nestedTestBean1", ntb1); + NestedTestBean ntb2 = new NestedTestBean(); + bf.registerSingleton("nestedTestBean2", ntb2); + + OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean"); + assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); + assertThat(bean.getTestBean2()).isSameAs(bf.getBean("testBean")); + assertThat(bean.getTestBean3()).isSameAs(bf.getBean("testBean")); + assertThat(bean.getTestBean4()).isSameAs(bf.getBean("testBean")); + assertThat(bean.getIndexedTestBean()).isSameAs(itb); + assertThat(bean.getNestedTestBeans().length).isEqualTo(2); + assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1); + assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2); + assertThat(bean.nestedTestBeansField.length).isEqualTo(2); + assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1); + assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2); + + bf.removeBeanDefinition("testBean"); + + bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean"); + assertThat(bean.getTestBean()).isNull(); + assertThat(bean.getTestBean2()).isNull(); + assertThat(bean.getTestBean3()).isNull(); + assertThat(bean.getTestBean4()).isNull(); + assertThat(bean.getIndexedTestBean()).isSameAs(itb); + assertThat(bean.getNestedTestBeans().length).isEqualTo(2); + assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1); + assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2); + assertThat(bean.nestedTestBeansField.length).isEqualTo(2); + assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1); + assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2); + + bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class)); + + bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean"); + assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); + assertThat(bean.getTestBean2()).isSameAs(bf.getBean("testBean")); + assertThat(bean.getTestBean3()).isSameAs(bf.getBean("testBean")); + assertThat(bean.getTestBean4()).isSameAs(bf.getBean("testBean")); + assertThat(bean.getIndexedTestBean()).isSameAs(itb); + assertThat(bean.getNestedTestBeans().length).isEqualTo(2); + assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1); + assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2); + assertThat(bean.nestedTestBeansField.length).isEqualTo(2); + assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1); + assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2); + } + @Test public void testOptionalCollectionResourceInjection() { RootBeanDefinition rbd = new RootBeanDefinition(OptionalCollectionResourceInjectionBean.class); - rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", rbd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -333,7 +448,7 @@ public void testOptionalCollectionResourceInjection() { @Test public void testOptionalCollectionResourceInjectionWithSingleElement() { RootBeanDefinition rbd = new RootBeanDefinition(OptionalCollectionResourceInjectionBean.class); - rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", rbd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -441,7 +556,7 @@ public void testAnnotationOrderedResourceInjection() { @Test public void testOrderedCollectionResourceInjection() { RootBeanDefinition rbd = new RootBeanDefinition(OptionalCollectionResourceInjectionBean.class); - rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", rbd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -476,7 +591,7 @@ public void testOrderedCollectionResourceInjection() { @Test public void testAnnotationOrderedCollectionResourceInjection() { RootBeanDefinition rbd = new RootBeanDefinition(OptionalCollectionResourceInjectionBean.class); - rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", rbd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -509,7 +624,7 @@ public void testAnnotationOrderedCollectionResourceInjection() { @Test public void testConstructorResourceInjection() { RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -533,10 +648,87 @@ public void testConstructorResourceInjection() { assertThat(bean.getBeanFactory()).isSameAs(bf); } + @Test + public void testConstructorResourceInjectionWithSingletonRemoval() { + RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); + bf.registerBeanDefinition("annotatedBean", bd); + TestBean tb = new TestBean(); + bf.registerSingleton("testBean", tb); + NestedTestBean ntb = new NestedTestBean(); + bf.registerSingleton("nestedTestBean", ntb); + + ConstructorResourceInjectionBean bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean"); + assertThat(bean.getTestBean()).isSameAs(tb); + assertThat(bean.getTestBean2()).isSameAs(tb); + assertThat(bean.getTestBean3()).isSameAs(tb); + assertThat(bean.getTestBean4()).isSameAs(tb); + assertThat(bean.getNestedTestBean()).isSameAs(ntb); + assertThat(bean.getBeanFactory()).isSameAs(bf); + + bf.destroySingleton("nestedTestBean"); + + bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean"); + assertThat(bean.getTestBean()).isSameAs(tb); + assertThat(bean.getTestBean2()).isSameAs(tb); + assertThat(bean.getTestBean3()).isSameAs(tb); + assertThat(bean.getTestBean4()).isSameAs(tb); + assertThat(bean.getNestedTestBean()).isNull(); + assertThat(bean.getBeanFactory()).isSameAs(bf); + + bf.registerSingleton("nestedTestBean", ntb); + + bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean"); + assertThat(bean.getTestBean()).isSameAs(tb); + assertThat(bean.getTestBean2()).isSameAs(tb); + assertThat(bean.getTestBean3()).isSameAs(tb); + assertThat(bean.getTestBean4()).isSameAs(tb); + assertThat(bean.getNestedTestBean()).isSameAs(ntb); + assertThat(bean.getBeanFactory()).isSameAs(bf); + } + + @Test + public void testConstructorResourceInjectionWithBeanDefinitionRemoval() { + RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); + bf.registerBeanDefinition("annotatedBean", bd); + TestBean tb = new TestBean(); + bf.registerSingleton("testBean", tb); + bf.registerBeanDefinition("nestedTestBean", new RootBeanDefinition(NestedTestBean.class)); + + ConstructorResourceInjectionBean bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean"); + assertThat(bean.getTestBean()).isSameAs(tb); + assertThat(bean.getTestBean2()).isSameAs(tb); + assertThat(bean.getTestBean3()).isSameAs(tb); + assertThat(bean.getTestBean4()).isSameAs(tb); + assertThat(bean.getNestedTestBean()).isSameAs(bf.getBean("nestedTestBean")); + assertThat(bean.getBeanFactory()).isSameAs(bf); + + bf.removeBeanDefinition("nestedTestBean"); + + bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean"); + assertThat(bean.getTestBean()).isSameAs(tb); + assertThat(bean.getTestBean2()).isSameAs(tb); + assertThat(bean.getTestBean3()).isSameAs(tb); + assertThat(bean.getTestBean4()).isSameAs(tb); + assertThat(bean.getNestedTestBean()).isNull(); + assertThat(bean.getBeanFactory()).isSameAs(bf); + + bf.registerBeanDefinition("nestedTestBean", new RootBeanDefinition(NestedTestBean.class)); + + bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean"); + assertThat(bean.getTestBean()).isSameAs(tb); + assertThat(bean.getTestBean2()).isSameAs(tb); + assertThat(bean.getTestBean3()).isSameAs(tb); + assertThat(bean.getTestBean4()).isSameAs(tb); + assertThat(bean.getNestedTestBean()).isSameAs(bf.getBean("nestedTestBean")); + assertThat(bean.getBeanFactory()).isSameAs(bf); + } + @Test public void testConstructorResourceInjectionWithNullFromFactoryBean() { RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -563,7 +755,7 @@ public void testConstructorResourceInjectionWithNullFromFactoryBean() { @Test public void testConstructorResourceInjectionWithNullFromFactoryMethod() { RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); RootBeanDefinition tb = new RootBeanDefinition(NullFactoryMethods.class); tb.setFactoryMethodName("createTestBean"); @@ -818,7 +1010,7 @@ public void testConstructorResourceInjectionWithMultipleCandidatesAndDefaultFall @Test public void testConstructorInjectionWithMap() { RootBeanDefinition bd = new RootBeanDefinition(MapConstructorInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb1 = new TestBean("tb1"); bf.registerSingleton("testBean1", tb1); @@ -840,7 +1032,7 @@ public void testConstructorInjectionWithMap() { @Test public void testFieldInjectionWithMap() { RootBeanDefinition bd = new RootBeanDefinition(MapFieldInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb1 = new TestBean("tb1"); TestBean tb2 = new TestBean("tb2"); @@ -865,7 +1057,7 @@ public void testFieldInjectionWithMap() { @Test public void testMethodInjectionWithMap() { RootBeanDefinition bd = new RootBeanDefinition(MapMethodInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -921,7 +1113,7 @@ public void testMethodInjectionWithMapAndNoMatches() { @Test public void testConstructorInjectionWithTypedMapAsBean() { RootBeanDefinition bd = new RootBeanDefinition(MapConstructorInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); MyTestBeanMap tbm = new MyTestBeanMap(); tbm.put("testBean1", new TestBean("tb1")); @@ -938,7 +1130,7 @@ public void testConstructorInjectionWithTypedMapAsBean() { @Test public void testConstructorInjectionWithPlainMapAsBean() { RootBeanDefinition bd = new RootBeanDefinition(MapConstructorInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); RootBeanDefinition tbm = new RootBeanDefinition(CollectionFactoryMethods.class); tbm.setUniqueFactoryMethodName("testBeanMap"); @@ -954,7 +1146,7 @@ public void testConstructorInjectionWithPlainMapAsBean() { @Test public void testConstructorInjectionWithCustomMapAsBean() { RootBeanDefinition bd = new RootBeanDefinition(CustomMapConstructorInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); RootBeanDefinition tbm = new RootBeanDefinition(CustomCollectionFactoryMethods.class); tbm.setUniqueFactoryMethodName("testBeanMap"); @@ -971,7 +1163,7 @@ public void testConstructorInjectionWithCustomMapAsBean() { @Test public void testConstructorInjectionWithPlainHashMapAsBean() { RootBeanDefinition bd = new RootBeanDefinition(QualifiedMapConstructorInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); bf.registerBeanDefinition("myTestBeanMap", new RootBeanDefinition(HashMap.class)); @@ -984,7 +1176,7 @@ public void testConstructorInjectionWithPlainHashMapAsBean() { @Test public void testConstructorInjectionWithTypedSetAsBean() { RootBeanDefinition bd = new RootBeanDefinition(SetConstructorInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); MyTestBeanSet tbs = new MyTestBeanSet(); tbs.add(new TestBean("tb1")); @@ -1001,7 +1193,7 @@ public void testConstructorInjectionWithTypedSetAsBean() { @Test public void testConstructorInjectionWithPlainSetAsBean() { RootBeanDefinition bd = new RootBeanDefinition(SetConstructorInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); RootBeanDefinition tbs = new RootBeanDefinition(CollectionFactoryMethods.class); tbs.setUniqueFactoryMethodName("testBeanSet"); @@ -1017,7 +1209,7 @@ public void testConstructorInjectionWithPlainSetAsBean() { @Test public void testConstructorInjectionWithCustomSetAsBean() { RootBeanDefinition bd = new RootBeanDefinition(CustomSetConstructorInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); RootBeanDefinition tbs = new RootBeanDefinition(CustomCollectionFactoryMethods.class); tbs.setUniqueFactoryMethodName("testBeanSet"); @@ -1135,7 +1327,7 @@ public void testObjectFactorySerialization() throws Exception { ObjectFactoryFieldInjectionBean bean = (ObjectFactoryFieldInjectionBean) bf.getBean("annotatedBean"); assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); - bean = (ObjectFactoryFieldInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean); + bean = SerializationTestUtils.serializeAndDeserialize(bean); assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); } @@ -1143,7 +1335,7 @@ public void testObjectFactorySerialization() throws Exception { public void testObjectProviderInjectionWithPrototype() { bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectProviderInjectionBean.class)); RootBeanDefinition tbd = new RootBeanDefinition(TestBean.class); - tbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + tbd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("testBean", tbd); ObjectProviderInjectionBean bean = (ObjectProviderInjectionBean) bf.getBean("annotatedBean"); @@ -1515,7 +1707,7 @@ public void testBeanAutowiredWithFactoryBean() { @Test public void testGenericsBasedFieldInjection() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); String sv = "X"; bf.registerSingleton("stringValue", sv); @@ -1560,7 +1752,7 @@ public void testGenericsBasedFieldInjection() { @Test public void testGenericsBasedFieldInjectionWithSubstitutedVariables() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBeanWithSubstitutedVariables.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); String sv = "X"; bf.registerSingleton("stringValue", sv); @@ -1605,7 +1797,7 @@ public void testGenericsBasedFieldInjectionWithSubstitutedVariables() { @Test public void testGenericsBasedFieldInjectionWithQualifiers() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBeanWithQualifiers.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); StringRepository sr = new StringRepository(); bf.registerSingleton("stringRepo", sr); @@ -1632,7 +1824,7 @@ public void testGenericsBasedFieldInjectionWithQualifiers() { @Test public void testGenericsBasedFieldInjectionWithMocks() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBeanWithQualifiers.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class); @@ -1671,7 +1863,7 @@ public void testGenericsBasedFieldInjectionWithMocks() { @Test public void testGenericsBasedFieldInjectionWithSimpleMatch() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBeanWithSimpleMatch.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); bf.registerSingleton("repo", new StringRepository()); @@ -1699,7 +1891,7 @@ public void testGenericsBasedFieldInjectionWithSimpleMatch() { @Test public void testGenericsBasedFactoryBeanInjectionWithBeanDefinition() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryFactoryBeanInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); bf.registerBeanDefinition("repoFactoryBean", new RootBeanDefinition(RepositoryFactoryBean.class)); @@ -1711,7 +1903,7 @@ public void testGenericsBasedFactoryBeanInjectionWithBeanDefinition() { @Test public void testGenericsBasedFactoryBeanInjectionWithSingletonBean() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryFactoryBeanInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); bf.registerSingleton("repoFactoryBean", new RepositoryFactoryBean<>()); @@ -1723,7 +1915,7 @@ public void testGenericsBasedFactoryBeanInjectionWithSingletonBean() { @Test public void testGenericsBasedFieldInjectionWithSimpleMatchAndMock() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBeanWithSimpleMatch.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class); @@ -1755,7 +1947,7 @@ public void testGenericsBasedFieldInjectionWithSimpleMatchAndMock() { @Test public void testGenericsBasedFieldInjectionWithSimpleMatchAndMockito() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBeanWithSimpleMatch.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); RootBeanDefinition rbd = new RootBeanDefinition(); @@ -1786,7 +1978,7 @@ public void testGenericsBasedFieldInjectionWithSimpleMatchAndMockito() { @Test public void testGenericsBasedMethodInjection() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryMethodInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); String sv = "X"; bf.registerSingleton("stringValue", sv); @@ -1831,7 +2023,7 @@ public void testGenericsBasedMethodInjection() { @Test public void testGenericsBasedMethodInjectionWithSubstitutedVariables() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryMethodInjectionBeanWithSubstitutedVariables.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); String sv = "X"; bf.registerSingleton("stringValue", sv); @@ -1876,7 +2068,7 @@ public void testGenericsBasedMethodInjectionWithSubstitutedVariables() { @Test public void testGenericsBasedConstructorInjection() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); StringRepository sr = new StringRepository(); bf.registerSingleton("stringRepo", sr); @@ -1904,7 +2096,7 @@ public void testGenericsBasedConstructorInjection() { @SuppressWarnings("rawtypes") public void testGenericsBasedConstructorInjectionWithNonTypedTarget() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); GenericRepository gr = new GenericRepository(); bf.registerSingleton("genericRepo", gr); @@ -1929,7 +2121,7 @@ public void testGenericsBasedConstructorInjectionWithNonTypedTarget() { @Test public void testGenericsBasedConstructorInjectionWithNonGenericTarget() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); SimpleRepository ngr = new SimpleRepository(); bf.registerSingleton("simpleRepo", ngr); @@ -1955,7 +2147,7 @@ public void testGenericsBasedConstructorInjectionWithNonGenericTarget() { @SuppressWarnings("rawtypes") public void testGenericsBasedConstructorInjectionWithMixedTargets() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); StringRepository sr = new StringRepository(); bf.registerSingleton("stringRepo", sr); @@ -1982,7 +2174,7 @@ public void testGenericsBasedConstructorInjectionWithMixedTargets() { @Test public void testGenericsBasedConstructorInjectionWithMixedTargetsIncludingNonGeneric() { RootBeanDefinition bd = new RootBeanDefinition(RepositoryConstructorInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); StringRepository sr = new StringRepository(); bf.registerSingleton("stringRepo", sr); @@ -2709,7 +2901,7 @@ public static class SelfInjectionBean { @SuppressWarnings("serial") - public static class SelfInjectionCollectionBean extends LinkedList { + public static class SelfInjectionCollectionBean extends ArrayList { @Autowired public SelfInjectionCollectionBean reference; @@ -2833,7 +3025,7 @@ public TestBean consumeUniqueTestBean() { } public List iterateTestBeans() { - List resolved = new LinkedList<>(); + List resolved = new ArrayList<>(); for (TestBean tb : this.testBeanProvider) { resolved.add(tb); } @@ -2841,7 +3033,7 @@ public List iterateTestBeans() { } public List forEachTestBeans() { - List resolved = new LinkedList<>(); + List resolved = new ArrayList<>(); this.testBeanProvider.forEach(resolved::add); return resolved; } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/CustomAutowireConfigurerTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/CustomAutowireConfigurerTests.java index 3454c28d9c22..02a87f6c0d4b 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/CustomAutowireConfigurerTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/CustomAutowireConfigurerTests.java @@ -25,7 +25,7 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * Unit tests for {@link CustomAutowireConfigurer}. diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.java index af5e0258e6b2..aa4c1cb20bff 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.java @@ -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. @@ -40,11 +40,11 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.IndexedTestBean; -import org.springframework.tests.sample.beans.NestedTestBean; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.util.SerializationTestUtils; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.IndexedTestBean; +import org.springframework.beans.testfixture.beans.NestedTestBean; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.core.testfixture.io.SerializationTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -94,7 +94,7 @@ public void testIncompleteBeanDefinition() { @Test public void testResourceInjection() { RootBeanDefinition bd = new RootBeanDefinition(ResourceInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -111,7 +111,7 @@ public void testResourceInjection() { @Test public void testExtendedResourceInjection() { RootBeanDefinition bd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -160,7 +160,7 @@ public void testExtendedResourceInjectionWithOverriding() { public void testExtendedResourceInjectionWithAtRequired() { bf.addBeanPostProcessor(new RequiredAnnotationBeanPostProcessor()); RootBeanDefinition bd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -179,7 +179,7 @@ public void testExtendedResourceInjectionWithAtRequired() { @Test public void testConstructorResourceInjection() { RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -236,7 +236,7 @@ public void testConstructorResourceInjectionWithMultipleCandidatesAndFallback() @Test public void testConstructorInjectionWithMap() { RootBeanDefinition bd = new RootBeanDefinition(MapConstructorInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb1 = new TestBean(); TestBean tb2 = new TestBean(); @@ -261,7 +261,7 @@ public void testConstructorInjectionWithMap() { @Test public void testFieldInjectionWithMap() { RootBeanDefinition bd = new RootBeanDefinition(MapFieldInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb1 = new TestBean(); TestBean tb2 = new TestBean(); @@ -286,7 +286,7 @@ public void testFieldInjectionWithMap() { @Test public void testMethodInjectionWithMap() { RootBeanDefinition bd = new RootBeanDefinition(MapMethodInjectionBean.class); - bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bd.setScope(BeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); @@ -394,7 +394,7 @@ public void testObjectFactoryWithBeanField() throws Exception { ObjectFactoryFieldInjectionBean bean = (ObjectFactoryFieldInjectionBean) bf.getBean("annotatedBean"); assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); - bean = (ObjectFactoryFieldInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean); + bean = SerializationTestUtils.serializeAndDeserialize(bean); assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); } @@ -406,7 +406,7 @@ public void testObjectFactoryWithBeanMethod() throws Exception { ObjectFactoryMethodInjectionBean bean = (ObjectFactoryMethodInjectionBean) bf.getBean("annotatedBean"); assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); - bean = (ObjectFactoryMethodInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean); + bean = SerializationTestUtils.serializeAndDeserialize(bean); assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); } @@ -418,7 +418,7 @@ public void testObjectFactoryWithTypedListField() throws Exception { ObjectFactoryListFieldInjectionBean bean = (ObjectFactoryListFieldInjectionBean) bf.getBean("annotatedBean"); assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); - bean = (ObjectFactoryListFieldInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean); + bean = SerializationTestUtils.serializeAndDeserialize(bean); assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); } @@ -430,7 +430,7 @@ public void testObjectFactoryWithTypedListMethod() throws Exception { ObjectFactoryListMethodInjectionBean bean = (ObjectFactoryListMethodInjectionBean) bf.getBean("annotatedBean"); assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); - bean = (ObjectFactoryListMethodInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean); + bean = SerializationTestUtils.serializeAndDeserialize(bean); assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); } @@ -442,7 +442,7 @@ public void testObjectFactoryWithTypedMapField() throws Exception { ObjectFactoryMapFieldInjectionBean bean = (ObjectFactoryMapFieldInjectionBean) bf.getBean("annotatedBean"); assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); - bean = (ObjectFactoryMapFieldInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean); + bean = SerializationTestUtils.serializeAndDeserialize(bean); assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); } @@ -454,7 +454,7 @@ public void testObjectFactoryWithTypedMapMethod() throws Exception { ObjectFactoryMapMethodInjectionBean bean = (ObjectFactoryMapMethodInjectionBean) bf.getBean("annotatedBean"); assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); - bean = (ObjectFactoryMapMethodInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean); + bean = SerializationTestUtils.serializeAndDeserialize(bean); assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean")); } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/LookupAnnotationTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/LookupAnnotationTests.java index 388b4a8bfa80..bd30b5b44c24 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/LookupAnnotationTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/LookupAnnotationTests.java @@ -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. @@ -19,9 +19,10 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -44,7 +45,7 @@ public void setup() { beanFactory.registerBeanDefinition("abstractBean", new RootBeanDefinition(AbstractBean.class)); beanFactory.registerBeanDefinition("beanConsumer", new RootBeanDefinition(BeanConsumer.class)); RootBeanDefinition tbd = new RootBeanDefinition(TestBean.class); - tbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + tbd.setScope(BeanDefinition.SCOPE_PROTOTYPE); beanFactory.registerBeanDefinition("testBean", tbd); } @@ -107,10 +108,23 @@ public void testWithEarlyInjection() { assertThat(beanFactory.getBean(BeanConsumer.class).abstractBean).isSameAs(bean); } + @Test // gh-25806 + public void testWithNullBean() { + RootBeanDefinition tbd = new RootBeanDefinition(TestBean.class, () -> null); + tbd.setScope(BeanDefinition.SCOPE_PROTOTYPE); + beanFactory.registerBeanDefinition("testBean", tbd); + + AbstractBean bean = beanFactory.getBean("beanConsumer", BeanConsumer.class).abstractBean; + assertThat(bean).isNotNull(); + Object expected = bean.get(); + assertThat(expected).isNull(); + assertThat(beanFactory.getBean(BeanConsumer.class).abstractBean).isSameAs(bean); + } + public static abstract class AbstractBean { - @Lookup + @Lookup("testBean") public abstract TestBean get(); @Lookup diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/CustomEditorConfigurerTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/CustomEditorConfigurerTests.java index 921e76100c5c..a94425ccf1aa 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/CustomEditorConfigurerTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/CustomEditorConfigurerTests.java @@ -33,7 +33,7 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.propertyeditors.CustomDateEditor; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/FieldRetrievingFactoryBeanTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/FieldRetrievingFactoryBeanTests.java index cc20dc84e4d3..b697500fb343 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/FieldRetrievingFactoryBeanTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/FieldRetrievingFactoryBeanTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,10 +22,10 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * Unit tests for {@link FieldRetrievingFactoryBean}. @@ -41,7 +41,7 @@ public void testStaticField() throws Exception { FieldRetrievingFactoryBean fr = new FieldRetrievingFactoryBean(); fr.setStaticField("java.sql.Connection.TRANSACTION_SERIALIZABLE"); fr.afterPropertiesSet(); - assertThat(fr.getObject()).isEqualTo(new Integer(Connection.TRANSACTION_SERIALIZABLE)); + assertThat(fr.getObject()).isEqualTo(Connection.TRANSACTION_SERIALIZABLE); } @Test @@ -49,7 +49,7 @@ public void testStaticFieldWithWhitespace() throws Exception { FieldRetrievingFactoryBean fr = new FieldRetrievingFactoryBean(); fr.setStaticField(" java.sql.Connection.TRANSACTION_SERIALIZABLE "); fr.afterPropertiesSet(); - assertThat(fr.getObject()).isEqualTo(new Integer(Connection.TRANSACTION_SERIALIZABLE)); + assertThat(fr.getObject()).isEqualTo(Connection.TRANSACTION_SERIALIZABLE); } @Test @@ -58,7 +58,7 @@ public void testStaticFieldViaClassAndFieldName() throws Exception { fr.setTargetClass(Connection.class); fr.setTargetField("TRANSACTION_SERIALIZABLE"); fr.afterPropertiesSet(); - assertThat(fr.getObject()).isEqualTo(new Integer(Connection.TRANSACTION_SERIALIZABLE)); + assertThat(fr.getObject()).isEqualTo(Connection.TRANSACTION_SERIALIZABLE); } @Test @@ -76,7 +76,7 @@ public void testNothingButBeanName() throws Exception { FieldRetrievingFactoryBean fr = new FieldRetrievingFactoryBean(); fr.setBeanName("java.sql.Connection.TRANSACTION_SERIALIZABLE"); fr.afterPropertiesSet(); - assertThat(fr.getObject()).isEqualTo(new Integer(Connection.TRANSACTION_SERIALIZABLE)); + assertThat(fr.getObject()).isEqualTo(Connection.TRANSACTION_SERIALIZABLE); } @Test @@ -115,7 +115,7 @@ public void testJustTargetObject() throws Exception { @Test public void testWithConstantOnClassWithPackageLevelVisibility() throws Exception { FieldRetrievingFactoryBean fr = new FieldRetrievingFactoryBean(); - fr.setBeanName("org.springframework.tests.sample.beans.PackageLevelVisibleBean.CONSTANT"); + fr.setBeanName("org.springframework.beans.testfixture.beans.PackageLevelVisibleBean.CONSTANT"); fr.afterPropertiesSet(); assertThat(fr.getObject()).isEqualTo("Wuby"); } @@ -127,8 +127,8 @@ public void testBeanNameSyntaxWithBeanFactory() throws Exception { qualifiedResource(FieldRetrievingFactoryBeanTests.class, "context.xml")); TestBean testBean = (TestBean) bf.getBean("testBean"); - assertThat(testBean.getSomeIntegerArray()[0]).isEqualTo(new Integer(Connection.TRANSACTION_SERIALIZABLE)); - assertThat(testBean.getSomeIntegerArray()[1]).isEqualTo(new Integer(Connection.TRANSACTION_SERIALIZABLE)); + assertThat(testBean.getSomeIntegerArray()[0]).isEqualTo(Connection.TRANSACTION_SERIALIZABLE); + assertThat(testBean.getSomeIntegerArray()[1]).isEqualTo(Connection.TRANSACTION_SERIALIZABLE); } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/ObjectFactoryCreatingFactoryBeanTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/ObjectFactoryCreatingFactoryBeanTests.java index 02066e98a455..7357ba9069dd 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/ObjectFactoryCreatingFactoryBeanTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/ObjectFactoryCreatingFactoryBeanTests.java @@ -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. @@ -28,13 +28,13 @@ import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.util.SerializationTestUtils; +import org.springframework.core.testfixture.io.SerializationTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * @author Colin Sampaleanu @@ -72,12 +72,11 @@ public void testFactoryOperation() { } @Test - @SuppressWarnings("rawtypes") public void testFactorySerialization() throws Exception { FactoryTestBean testBean = beanFactory.getBean("factoryTestBean", FactoryTestBean.class); ObjectFactory objectFactory = testBean.getObjectFactory(); - objectFactory = (ObjectFactory) SerializationTestUtils.serializeAndDeserialize(objectFactory); + objectFactory = SerializationTestUtils.serializeAndDeserialize(objectFactory); Date date1 = (Date) objectFactory.getObject(); Date date2 = (Date) objectFactory.getObject(); @@ -95,12 +94,11 @@ public void testProviderOperation() { } @Test - @SuppressWarnings("rawtypes") public void testProviderSerialization() throws Exception { ProviderTestBean testBean = beanFactory.getBean("providerTestBean", ProviderTestBean.class); Provider provider = testBean.getProvider(); - provider = (Provider) SerializationTestUtils.serializeAndDeserialize(provider); + provider = SerializationTestUtils.serializeAndDeserialize(provider); Date date1 = (Date) provider.get(); Date date2 = (Date) provider.get(); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertiesFactoryBeanTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertiesFactoryBeanTests.java index e7b68287893d..27a00cb5c47c 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertiesFactoryBeanTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertiesFactoryBeanTests.java @@ -23,7 +23,7 @@ import org.springframework.core.io.Resource; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * Unit tests for {@link PropertiesFactoryBean}. diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyPathFactoryBeanTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyPathFactoryBeanTests.java index 0c7fcc4a4c1f..bc0c7cce84f7 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyPathFactoryBeanTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyPathFactoryBeanTests.java @@ -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. @@ -20,12 +20,12 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.Resource; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * Unit tests for {@link PropertyPathFactoryBean}. @@ -43,9 +43,9 @@ public class PropertyPathFactoryBeanTests { public void testPropertyPathFactoryBeanWithSingletonResult() { DefaultListableBeanFactory xbf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(CONTEXT); - assertThat(xbf.getBean("propertyPath1")).isEqualTo(new Integer(12)); - assertThat(xbf.getBean("propertyPath2")).isEqualTo(new Integer(11)); - assertThat(xbf.getBean("tb.age")).isEqualTo(new Integer(10)); + assertThat(xbf.getBean("propertyPath1")).isEqualTo(12); + assertThat(xbf.getBean("propertyPath2")).isEqualTo(11); + assertThat(xbf.getBean("tb.age")).isEqualTo(10); assertThat(xbf.getType("otb.spouse")).isEqualTo(ITestBean.class); Object result1 = xbf.getBean("otb.spouse"); Object result2 = xbf.getBean("otb.spouse"); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.java index ac0643b3f28c..13d8b138ff43 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.java @@ -24,9 +24,9 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyResourceConfigurerTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyResourceConfigurerTests.java index 9d5b3909ce60..5dd76865de42 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyResourceConfigurerTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyResourceConfigurerTests.java @@ -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. @@ -39,15 +39,15 @@ import org.springframework.beans.factory.support.ManagedMap; import org.springframework.beans.factory.support.ManagedSet; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.testfixture.beans.IndexedTestBean; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.Resource; -import org.springframework.tests.sample.beans.IndexedTestBean; -import org.springframework.tests.sample.beans.TestBean; import org.springframework.util.StringUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * Unit tests for various {@link PropertyResourceConfigurer} implementations including: @@ -414,7 +414,7 @@ private void doTestPropertyPlaceholderConfigurer(boolean parentChildSeparation) assertThat(tb2.getSomeSet().size()).isEqualTo(3); assertThat(tb2.getSomeSet().contains("na98me")).isTrue(); assertThat(tb2.getSomeSet().contains(tb2)).isTrue(); - assertThat(tb2.getSomeSet().contains(new Integer(98))).isTrue(); + assertThat(tb2.getSomeSet().contains(98)).isTrue(); assertThat(tb2.getSomeMap().size()).isEqualTo(6); assertThat(tb2.getSomeMap().get("key98")).isEqualTo("98"); assertThat(tb2.getSomeMap().get("key98ref")).isEqualTo(tb2); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/SimpleScopeTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/SimpleScopeTests.java index 297c4a655ca6..c66e65e2489f 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/SimpleScopeTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/SimpleScopeTests.java @@ -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,7 +16,7 @@ package org.springframework.beans.factory.config; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -25,10 +25,10 @@ import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * Simple test to illustrate and verify scope usage. @@ -47,7 +47,7 @@ public void setup() { beanFactory = new DefaultListableBeanFactory(); Scope scope = new NoOpScope() { private int index; - private List objects = new LinkedList<>(); { + private List objects = new ArrayList<>(); { objects.add(new TestBean()); objects.add(new TestBean()); } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java index 0bacf2162582..50c7f903a40a 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,19 +16,23 @@ package org.springframework.beans.factory.config; +import java.net.URL; +import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Test; +import org.yaml.snakeyaml.constructor.ConstructorException; import org.yaml.snakeyaml.parser.ParserException; import org.yaml.snakeyaml.scanner.ScannerException; import org.springframework.core.io.ByteArrayResource; -import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.entry; /** * Tests for {@link YamlProcessor}. @@ -36,15 +40,17 @@ * @author Dave Syer * @author Juergen Hoeller * @author Sam Brannen + * @author Brian Clozel */ -public class YamlProcessorTests { +class YamlProcessorTests { - private final YamlProcessor processor = new YamlProcessor() {}; + private final YamlProcessor processor = new YamlProcessor() { + }; @Test - public void arrayConvertedToIndexedBeanReference() { - this.processor.setResources(new ByteArrayResource("foo: bar\nbar: [1,2,3]".getBytes())); + void arrayConvertedToIndexedBeanReference() { + setYaml("foo: bar\nbar: [1,2,3]"); this.processor.process((properties, map) -> { assertThat(properties.size()).isEqualTo(4); assertThat(properties.get("foo")).isEqualTo("bar"); @@ -59,30 +65,30 @@ public void arrayConvertedToIndexedBeanReference() { } @Test - public void stringResource() { - this.processor.setResources(new ByteArrayResource("foo # a document that is a literal".getBytes())); + void stringResource() { + setYaml("foo # a document that is a literal"); this.processor.process((properties, map) -> assertThat(map.get("document")).isEqualTo("foo")); } @Test - public void badDocumentStart() { - this.processor.setResources(new ByteArrayResource("foo # a document\nbar: baz".getBytes())); + void badDocumentStart() { + setYaml("foo # a document\nbar: baz"); assertThatExceptionOfType(ParserException.class) .isThrownBy(() -> this.processor.process((properties, map) -> {})) .withMessageContaining("line 2, column 1"); } @Test - public void badResource() { - this.processor.setResources(new ByteArrayResource("foo: bar\ncd\nspam:\n foo: baz".getBytes())); + void badResource() { + setYaml("foo: bar\ncd\nspam:\n foo: baz"); assertThatExceptionOfType(ScannerException.class) - .isThrownBy(() -> this.processor.process((properties, map) -> {})) - .withMessageContaining("line 3, column 1"); + .isThrownBy(() -> this.processor.process((properties, map) -> {})) + .withMessageContaining("line 3, column 1"); } @Test - public void mapConvertedToIndexedBeanReference() { - this.processor.setResources(new ByteArrayResource("foo: bar\nbar:\n spam: bucket".getBytes())); + void mapConvertedToIndexedBeanReference() { + setYaml("foo: bar\nbar:\n spam: bucket"); this.processor.process((properties, map) -> { assertThat(properties.get("bar.spam")).isEqualTo("bucket"); assertThat(properties).hasSize(2); @@ -90,8 +96,8 @@ public void mapConvertedToIndexedBeanReference() { } @Test - public void integerKeyBehaves() { - this.processor.setResources(new ByteArrayResource("foo: bar\n1: bar".getBytes())); + void integerKeyBehaves() { + setYaml("foo: bar\n1: bar"); this.processor.process((properties, map) -> { assertThat(properties.get("[1]")).isEqualTo("bar"); assertThat(properties).hasSize(2); @@ -99,8 +105,8 @@ public void integerKeyBehaves() { } @Test - public void integerDeepKeyBehaves() { - this.processor.setResources(new ByteArrayResource("foo:\n 1: bar".getBytes())); + void integerDeepKeyBehaves() { + setYaml("foo:\n 1: bar"); this.processor.process((properties, map) -> { assertThat(properties.get("foo[1]")).isEqualTo("bar"); assertThat(properties).hasSize(1); @@ -109,8 +115,8 @@ public void integerDeepKeyBehaves() { @Test @SuppressWarnings("unchecked") - public void flattenedMapIsSameAsPropertiesButOrdered() { - this.processor.setResources(new ByteArrayResource("cat: dog\nfoo: bar\nbar:\n spam: bucket".getBytes())); + void flattenedMapIsSameAsPropertiesButOrdered() { + setYaml("cat: dog\nfoo: bar\nbar:\n spam: bucket"); this.processor.process((properties, map) -> { Map flattenedMap = processor.getFlattenedMap(map); assertThat(flattenedMap).isInstanceOf(LinkedHashMap.class); @@ -124,8 +130,8 @@ public void flattenedMapIsSameAsPropertiesButOrdered() { Map bar = (Map) map.get("bar"); assertThat(bar.get("spam")).isEqualTo("bucket"); - List keysFromProperties = properties.keySet().stream().collect(toList()); - List keysFromFlattenedMap = flattenedMap.keySet().stream().collect(toList()); + List keysFromProperties = new ArrayList<>(properties.keySet()); + List keysFromFlattenedMap = new ArrayList<>(flattenedMap.keySet()); assertThat(keysFromProperties).containsExactlyInAnyOrderElementsOf(keysFromFlattenedMap); // Keys in the Properties object are sorted. assertThat(keysFromProperties).containsExactly("bar.spam", "cat", "foo"); @@ -134,4 +140,52 @@ public void flattenedMapIsSameAsPropertiesButOrdered() { }); } + @Test + void standardTypesSupportedByDefault() throws Exception { + setYaml("value: !!set\n ? first\n ? second"); + this.processor.process((properties, map) -> { + assertThat(properties).containsExactly(entry("value[0]", "first"), entry("value[1]", "second")); + assertThat(map.get("value")).isInstanceOf(Set.class); + Set set = (Set) map.get("value"); + assertThat(set).containsExactly("first", "second"); + }); + } + + @Test + void customTypeNotSupportedByDefault() throws Exception { + URL url = new URL("https://localhost:9000/"); + setYaml("value: !!java.net.URL [\"" + url + "\"]"); + assertThatExceptionOfType(ConstructorException.class) + .isThrownBy(() -> this.processor.process((properties, map) -> {})) + .withMessageContaining("Unsupported type encountered in YAML document: java.net.URL"); + } + + @Test + void customTypesSupportedDueToExplicitConfiguration() throws Exception { + this.processor.setSupportedTypes(URL.class, String.class); + + URL url = new URL("https://localhost:9000/"); + setYaml("value: !!java.net.URL [!!java.lang.String [\"" + url + "\"]]"); + + this.processor.process((properties, map) -> { + assertThat(properties).containsExactly(entry("value", url)); + assertThat(map).containsExactly(entry("value", url)); + }); + } + + @Test + void customTypeNotSupportedDueToExplicitConfiguration() { + this.processor.setSupportedTypes(List.class); + + setYaml("value: !!java.net.URL [\"https://localhost:9000/\"]"); + + assertThatExceptionOfType(ConstructorException.class) + .isThrownBy(() -> this.processor.process((properties, map) -> {})) + .withMessageContaining("Unsupported type encountered in YAML document: java.net.URL"); + } + + private void setYaml(String yaml) { + this.processor.setResources(new ByteArrayResource(yaml.getBytes())); + } + } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBeanTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBeanTests.java index 828ac0808184..c484cba81e2b 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBeanTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBeanTests.java @@ -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. @@ -24,7 +24,6 @@ import org.yaml.snakeyaml.constructor.DuplicateKeyException; import org.yaml.snakeyaml.scanner.ScannerException; -import org.springframework.beans.factory.config.YamlProcessor.DocumentMatcher; import org.springframework.beans.factory.config.YamlProcessor.MatchStatus; import org.springframework.beans.factory.config.YamlProcessor.ResolutionMethod; import org.springframework.core.io.ByteArrayResource; @@ -39,10 +38,10 @@ * @author Dave Syer * @author Juergen Hoeller */ -public class YamlPropertiesFactoryBeanTests { +class YamlPropertiesFactoryBeanTests { @Test - public void loadResource() { + void loadResource() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(new ByteArrayResource("foo: bar\nspam:\n foo: baz".getBytes())); Properties properties = factory.getObject(); @@ -51,7 +50,7 @@ public void loadResource() { } @Test - public void badResource() { + void badResource() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(new ByteArrayResource("foo: bar\ncd\nspam:\n foo: baz".getBytes())); assertThatExceptionOfType(ScannerException.class) @@ -60,7 +59,7 @@ public void badResource() { } @Test - public void loadResourcesWithOverride() { + void loadResourcesWithOverride() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources( new ByteArrayResource("foo: bar\nspam:\n foo: baz".getBytes()), @@ -72,7 +71,7 @@ public void loadResourcesWithOverride() { } @Test - public void loadResourcesWithInternalOverride() { + void loadResourcesWithInternalOverride() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(new ByteArrayResource( "foo: bar\nspam:\n foo: baz\nfoo: bucket".getBytes())); @@ -80,7 +79,7 @@ public void loadResourcesWithInternalOverride() { } @Test - public void loadResourcesWithNestedInternalOverride() { + void loadResourcesWithNestedInternalOverride() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(new ByteArrayResource( "foo:\n bar: spam\n foo: baz\nbreak: it\nfoo: bucket".getBytes())); @@ -88,7 +87,7 @@ public void loadResourcesWithNestedInternalOverride() { } @Test - public void loadResourceWithMultipleDocuments() { + void loadResourceWithMultipleDocuments() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(new ByteArrayResource( "foo: bar\nspam: baz\n---\nfoo: bag".getBytes())); @@ -98,7 +97,7 @@ public void loadResourceWithMultipleDocuments() { } @Test - public void loadResourceWithSelectedDocuments() { + void loadResourceWithSelectedDocuments() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(new ByteArrayResource( "foo: bar\nspam: baz\n---\nfoo: bag\nspam: bad".getBytes())); @@ -110,7 +109,7 @@ public void loadResourceWithSelectedDocuments() { } @Test - public void loadResourceWithDefaultMatch() { + void loadResourceWithDefaultMatch() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setMatchDefault(true); factory.setResources(new ByteArrayResource( @@ -129,20 +128,17 @@ public void loadResourceWithDefaultMatch() { } @Test - public void loadResourceWithoutDefaultMatch() { + void loadResourceWithoutDefaultMatch() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setMatchDefault(false); factory.setResources(new ByteArrayResource( "one: two\n---\nfoo: bar\nspam: baz\n---\nfoo: bag\nspam: bad".getBytes())); - factory.setDocumentMatchers(new DocumentMatcher() { - @Override - public MatchStatus matches(Properties properties) { + factory.setDocumentMatchers(properties -> { if (!properties.containsKey("foo")) { return MatchStatus.ABSTAIN; } return ("bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND : MatchStatus.NOT_FOUND); - } }); Properties properties = factory.getObject(); assertThat(properties.getProperty("foo")).isEqualTo("bag"); @@ -151,7 +147,7 @@ public MatchStatus matches(Properties properties) { } @Test - public void loadResourceWithDefaultMatchSkippingMissedMatch() { + void loadResourceWithDefaultMatchSkippingMissedMatch() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setMatchDefault(true); factory.setResources(new ByteArrayResource( @@ -170,7 +166,7 @@ public void loadResourceWithDefaultMatchSkippingMissedMatch() { } @Test - public void loadNonExistentResource() { + void loadNonExistentResource() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResolutionMethod(ResolutionMethod.OVERRIDE_AND_IGNORE); factory.setResources(new ClassPathResource("no-such-file.yml")); @@ -179,7 +175,7 @@ public void loadNonExistentResource() { } @Test - public void loadNull() { + void loadNull() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(new ByteArrayResource("foo: bar\nspam:".getBytes())); Properties properties = factory.getObject(); @@ -188,7 +184,7 @@ public void loadNull() { } @Test - public void loadEmptyArrayValue() { + void loadEmptyArrayValue() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(new ByteArrayResource("a: alpha\ntest: []".getBytes())); Properties properties = factory.getObject(); @@ -197,7 +193,7 @@ public void loadEmptyArrayValue() { } @Test - public void loadArrayOfString() { + void loadArrayOfString() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(new ByteArrayResource("foo:\n- bar\n- baz".getBytes())); Properties properties = factory.getObject(); @@ -207,7 +203,7 @@ public void loadArrayOfString() { } @Test - public void loadArrayOfInteger() { + void loadArrayOfInteger() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(new ByteArrayResource("foo:\n- 1\n- 2".getBytes())); Properties properties = factory.getObject(); @@ -217,7 +213,7 @@ public void loadArrayOfInteger() { } @Test - public void loadArrayOfObject() { + void loadArrayOfObject() { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(new ByteArrayResource( "foo:\n- bar:\n spam: crap\n- baz\n- one: two\n three: four".getBytes() @@ -232,7 +228,7 @@ public void loadArrayOfObject() { @Test @SuppressWarnings("unchecked") - public void yaml() { + void yaml() { Yaml yaml = new Yaml(); Map map = yaml.loadAs("foo: bar\nspam:\n foo: baz", Map.class); assertThat(map.get("foo")).isEqualTo("bar"); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/parsing/CustomProblemReporterTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/parsing/CustomProblemReporterTests.java index 33caa90d5734..34abf223af41 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/parsing/CustomProblemReporterTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/parsing/CustomProblemReporterTests.java @@ -24,10 +24,10 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestResourceUtils.qualifiedResource; +import static org.springframework.core.testfixture.io.ResourceTestUtils.qualifiedResource; /** * @author Rob Harrop diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionBuilderTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionBuilderTests.java index 6d416cdcf798..ec157df512c2 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionBuilderTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionBuilderTests.java @@ -21,7 +21,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionTests.java index a7af6e8f2924..de770309e058 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; @@ -73,19 +73,19 @@ public void beanDefinitionEqualityWithPropertyValues() { public void beanDefinitionEqualityWithConstructorArguments() { RootBeanDefinition bd = new RootBeanDefinition(TestBean.class); bd.getConstructorArgumentValues().addGenericArgumentValue("test"); - bd.getConstructorArgumentValues().addIndexedArgumentValue(1, new Integer(5)); + bd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5); RootBeanDefinition otherBd = new RootBeanDefinition(TestBean.class); otherBd.getConstructorArgumentValues().addGenericArgumentValue("test"); boolean condition3 = !bd.equals(otherBd); assertThat(condition3).isTrue(); boolean condition2 = !otherBd.equals(bd); assertThat(condition2).isTrue(); - otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, new Integer(9)); + otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 9); boolean condition1 = !bd.equals(otherBd); assertThat(condition1).isTrue(); boolean condition = !otherBd.equals(bd); assertThat(condition).isTrue(); - otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, new Integer(5)); + otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5); assertThat(bd.equals(otherBd)).isTrue(); assertThat(otherBd.equals(bd)).isTrue(); assertThat(bd.hashCode() == otherBd.hashCode()).isTrue(); @@ -95,20 +95,20 @@ public void beanDefinitionEqualityWithConstructorArguments() { public void beanDefinitionEqualityWithTypedConstructorArguments() { RootBeanDefinition bd = new RootBeanDefinition(TestBean.class); bd.getConstructorArgumentValues().addGenericArgumentValue("test", "int"); - bd.getConstructorArgumentValues().addIndexedArgumentValue(1, new Integer(5), "long"); + bd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5, "long"); RootBeanDefinition otherBd = new RootBeanDefinition(TestBean.class); otherBd.getConstructorArgumentValues().addGenericArgumentValue("test", "int"); - otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, new Integer(5)); + otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5); boolean condition3 = !bd.equals(otherBd); assertThat(condition3).isTrue(); boolean condition2 = !otherBd.equals(bd); assertThat(condition2).isTrue(); - otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, new Integer(5), "int"); + otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5, "int"); boolean condition1 = !bd.equals(otherBd); assertThat(condition1).isTrue(); boolean condition = !otherBd.equals(bd); assertThat(condition).isTrue(); - otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, new Integer(5), "long"); + otherBd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5, "long"); assertThat(bd.equals(otherBd)).isTrue(); assertThat(otherBd.equals(bd)).isTrue(); assertThat(bd.hashCode() == otherBd.hashCode()).isTrue(); @@ -133,6 +133,16 @@ public void genericBeanDefinitionEquality() { assertThat(bd.equals(otherBd)).isTrue(); assertThat(otherBd.equals(bd)).isTrue(); assertThat(bd.hashCode() == otherBd.hashCode()).isTrue(); + + bd.getPropertyValues(); + assertThat(bd.equals(otherBd)).isTrue(); + assertThat(otherBd.equals(bd)).isTrue(); + assertThat(bd.hashCode() == otherBd.hashCode()).isTrue(); + + bd.getConstructorArgumentValues(); + assertThat(bd.equals(otherBd)).isTrue(); + assertThat(otherBd.equals(bd)).isTrue(); + assertThat(bd.hashCode() == otherBd.hashCode()).isTrue(); } @Test @@ -160,7 +170,7 @@ public void beanDefinitionHolderEquality() { public void beanDefinitionMerging() { RootBeanDefinition bd = new RootBeanDefinition(TestBean.class); bd.getConstructorArgumentValues().addGenericArgumentValue("test"); - bd.getConstructorArgumentValues().addIndexedArgumentValue(1, new Integer(5)); + bd.getConstructorArgumentValues().addIndexedArgumentValue(1, 5); bd.getPropertyValues().add("name", "myName"); bd.getPropertyValues().add("age", "99"); bd.setQualifiedElement(getClass()); @@ -174,8 +184,8 @@ public void beanDefinitionMerging() { assertThat(mergedBd.getPropertyValues().size()).isEqualTo(2); assertThat(mergedBd).isEqualTo(bd); - mergedBd.getConstructorArgumentValues().getArgumentValue(1, null).setValue(new Integer(9)); - assertThat(bd.getConstructorArgumentValues().getArgumentValue(1, null).getValue()).isEqualTo(new Integer(5)); + mergedBd.getConstructorArgumentValues().getArgumentValue(1, null).setValue(9); + assertThat(bd.getConstructorArgumentValues().getArgumentValue(1, null).getValue()).isEqualTo(5); assertThat(bd.getQualifiedElement()).isEqualTo(getClass()); } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java index d848f90a7e5e..e3ba7b5d93f1 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java @@ -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. @@ -26,7 +26,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -43,21 +42,21 @@ import org.springframework.beans.factory.config.TypedStringValue; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.beans.propertyeditors.CustomNumberEditor; +import org.springframework.beans.testfixture.beans.GenericBean; +import org.springframework.beans.testfixture.beans.GenericIntegerBean; +import org.springframework.beans.testfixture.beans.GenericSetOfIntegerBean; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.OverridingClassLoader; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.Order; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.UrlResource; -import org.springframework.tests.EnabledForTestGroups; -import org.springframework.tests.sample.beans.GenericBean; -import org.springframework.tests.sample.beans.GenericIntegerBean; -import org.springframework.tests.sample.beans.GenericSetOfIntegerBean; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.core.testfixture.EnabledForTestGroups; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.tests.TestGroup.LONG_RUNNING; +import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING; /** * @author Juergen Hoeller @@ -80,8 +79,8 @@ public void testGenericSetProperty() { bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getIntegerSet().contains(new Integer(4))).isTrue(); - assertThat(gb.getIntegerSet().contains(new Integer(5))).isTrue(); + assertThat(gb.getIntegerSet().contains(4)).isTrue(); + assertThat(gb.getIntegerSet().contains(5)).isTrue(); } @Test @@ -159,8 +158,8 @@ public void testGenericMapProperty() { bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(new Integer(5)); - assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(new Integer(7)); + assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(5); + assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(7); } @Test @@ -191,23 +190,23 @@ public void testGenericSetConstructor() { bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getIntegerSet().contains(new Integer(4))).isTrue(); - assertThat(gb.getIntegerSet().contains(new Integer(5))).isTrue(); + assertThat(gb.getIntegerSet().contains(4)).isTrue(); + assertThat(gb.getIntegerSet().contains(5)).isTrue(); } @Test public void testGenericSetConstructorWithAutowiring() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - bf.registerSingleton("integer1", new Integer(4)); - bf.registerSingleton("integer2", new Integer(5)); + bf.registerSingleton("integer1", 4); + bf.registerSingleton("integer2", 5); RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); rbd.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getIntegerSet().contains(new Integer(4))).isTrue(); - assertThat(gb.getIntegerSet().contains(new Integer(5))).isTrue(); + assertThat(gb.getIntegerSet().contains(4)).isTrue(); + assertThat(gb.getIntegerSet().contains(5)).isTrue(); } @Test @@ -239,8 +238,8 @@ public void testGenericSetListConstructor() throws Exception { bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getIntegerSet().contains(new Integer(4))).isTrue(); - assertThat(gb.getIntegerSet().contains(new Integer(5))).isTrue(); + assertThat(gb.getIntegerSet().contains(4)).isTrue(); + assertThat(gb.getIntegerSet().contains(5)).isTrue(); assertThat(gb.getResourceList().get(0)).isEqualTo(new UrlResource("http://localhost:8080")); assertThat(gb.getResourceList().get(1)).isEqualTo(new UrlResource("http://localhost:9090")); } @@ -248,8 +247,8 @@ public void testGenericSetListConstructor() throws Exception { @Test public void testGenericSetListConstructorWithAutowiring() throws Exception { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - bf.registerSingleton("integer1", new Integer(4)); - bf.registerSingleton("integer2", new Integer(5)); + bf.registerSingleton("integer1", 4); + bf.registerSingleton("integer2", 5); bf.registerSingleton("resource1", new UrlResource("http://localhost:8080")); bf.registerSingleton("resource2", new UrlResource("http://localhost:9090")); @@ -258,8 +257,8 @@ public void testGenericSetListConstructorWithAutowiring() throws Exception { bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getIntegerSet().contains(new Integer(4))).isTrue(); - assertThat(gb.getIntegerSet().contains(new Integer(5))).isTrue(); + assertThat(gb.getIntegerSet().contains(4)).isTrue(); + assertThat(gb.getIntegerSet().contains(5)).isTrue(); assertThat(gb.getResourceList().get(0)).isEqualTo(new UrlResource("http://localhost:8080")); assertThat(gb.getResourceList().get(1)).isEqualTo(new UrlResource("http://localhost:9090")); } @@ -296,10 +295,10 @@ public void testGenericSetMapConstructor() { bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getIntegerSet().contains(new Integer(4))).isTrue(); - assertThat(gb.getIntegerSet().contains(new Integer(5))).isTrue(); - assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(new Integer(5)); - assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(new Integer(7)); + assertThat(gb.getIntegerSet().contains(4)).isTrue(); + assertThat(gb.getIntegerSet().contains(5)).isTrue(); + assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(5); + assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(7); } @Test @@ -316,8 +315,8 @@ public void testGenericMapResourceConstructor() throws Exception { bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(new Integer(5)); - assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(new Integer(7)); + assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(5); + assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(7); assertThat(gb.getResourceList().get(0)).isEqualTo(new UrlResource("http://localhost:8080")); } @@ -343,8 +342,8 @@ public void testGenericMapMapConstructor() { assertThat(gb.getPlainMap().get("1")).isEqualTo("0"); assertThat(gb.getPlainMap().get("2")).isEqualTo("3"); assertThat(gb.getShortMap().size()).isEqualTo(2); - assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(new Integer(5)); - assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(new Integer(7)); + assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(5); + assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(7); } @Test @@ -366,8 +365,8 @@ public void testGenericMapMapConstructorWithSameRefAndConversion() { assertThat(gb.getPlainMap().get("1")).isEqualTo("0"); assertThat(gb.getPlainMap().get("2")).isEqualTo("3"); assertThat(gb.getShortMap().size()).isEqualTo(2); - assertThat(gb.getShortMap().get(new Short("1"))).isEqualTo(new Integer(0)); - assertThat(gb.getShortMap().get(new Short("2"))).isEqualTo(new Integer(3)); + assertThat(gb.getShortMap().get(new Short("1"))).isEqualTo(0); + assertThat(gb.getShortMap().get(new Short("2"))).isEqualTo(3); } @Test @@ -376,8 +375,8 @@ public void testGenericMapMapConstructorWithSameRefAndNoConversion() { RootBeanDefinition rbd = new RootBeanDefinition(GenericBean.class); Map input = new HashMap<>(); - input.put(new Short((short) 1), new Integer(0)); - input.put(new Short((short) 2), new Integer(3)); + input.put(new Short((short) 1), 0); + input.put(new Short((short) 2), 3); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); rbd.getConstructorArgumentValues().addGenericArgumentValue(input); @@ -386,8 +385,8 @@ public void testGenericMapMapConstructorWithSameRefAndNoConversion() { assertThat(gb.getShortMap()).isSameAs(gb.getPlainMap()); assertThat(gb.getShortMap().size()).isEqualTo(2); - assertThat(gb.getShortMap().get(new Short("1"))).isEqualTo(new Integer(0)); - assertThat(gb.getShortMap().get(new Short("2"))).isEqualTo(new Integer(3)); + assertThat(gb.getShortMap().get(new Short("1"))).isEqualTo(0); + assertThat(gb.getShortMap().get(new Short("2"))).isEqualTo(3); } @Test @@ -403,8 +402,8 @@ public void testGenericMapWithKeyTypeConstructor() { bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getLongMap().get(new Long("4"))).isEqualTo("5"); - assertThat(gb.getLongMap().get(new Long("6"))).isEqualTo("7"); + assertThat(gb.getLongMap().get(4L)).isEqualTo("5"); + assertThat(gb.getLongMap().get(6L)).isEqualTo("7"); } @Test @@ -420,7 +419,7 @@ public void registerCustomEditors(PropertyEditorRegistry registry) { Map> input = new HashMap<>(); HashSet value1 = new HashSet<>(); - value1.add(new Integer(1)); + value1.add(1); input.put("1", value1); ArrayList value2 = new ArrayList<>(); value2.add(Boolean.TRUE); @@ -431,9 +430,9 @@ public void registerCustomEditors(PropertyEditorRegistry registry) { bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - boolean condition1 = gb.getCollectionMap().get(new Integer(1)) instanceof HashSet; + boolean condition1 = gb.getCollectionMap().get(1) instanceof HashSet; assertThat(condition1).isTrue(); - boolean condition = gb.getCollectionMap().get(new Integer(2)) instanceof ArrayList; + boolean condition = gb.getCollectionMap().get(2) instanceof ArrayList; assertThat(condition).isTrue(); } @@ -452,8 +451,8 @@ public void testGenericSetFactoryMethod() { bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getIntegerSet().contains(new Integer(4))).isTrue(); - assertThat(gb.getIntegerSet().contains(new Integer(5))).isTrue(); + assertThat(gb.getIntegerSet().contains(4)).isTrue(); + assertThat(gb.getIntegerSet().contains(5)).isTrue(); } @Test @@ -474,8 +473,8 @@ public void testGenericSetListFactoryMethod() throws Exception { bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getIntegerSet().contains(new Integer(4))).isTrue(); - assertThat(gb.getIntegerSet().contains(new Integer(5))).isTrue(); + assertThat(gb.getIntegerSet().contains(4)).isTrue(); + assertThat(gb.getIntegerSet().contains(5)).isTrue(); assertThat(gb.getResourceList().get(0)).isEqualTo(new UrlResource("http://localhost:8080")); assertThat(gb.getResourceList().get(1)).isEqualTo(new UrlResource("http://localhost:9090")); } @@ -498,10 +497,10 @@ public void testGenericSetMapFactoryMethod() { bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getIntegerSet().contains(new Integer(4))).isTrue(); - assertThat(gb.getIntegerSet().contains(new Integer(5))).isTrue(); - assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(new Integer(5)); - assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(new Integer(7)); + assertThat(gb.getIntegerSet().contains(4)).isTrue(); + assertThat(gb.getIntegerSet().contains(5)).isTrue(); + assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(5); + assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(7); } @Test @@ -519,8 +518,8 @@ public void testGenericMapResourceFactoryMethod() throws Exception { bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(new Integer(5)); - assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(new Integer(7)); + assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(5); + assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(7); assertThat(gb.getResourceList().get(0)).isEqualTo(new UrlResource("http://localhost:8080")); } @@ -544,8 +543,8 @@ public void testGenericMapMapFactoryMethod() { assertThat(gb.getPlainMap().get("1")).isEqualTo("0"); assertThat(gb.getPlainMap().get("2")).isEqualTo("3"); - assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(new Integer(5)); - assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(new Integer(7)); + assertThat(gb.getShortMap().get(new Short("4"))).isEqualTo(5); + assertThat(gb.getShortMap().get(new Short("6"))).isEqualTo(7); } @Test @@ -580,7 +579,7 @@ public void registerCustomEditors(PropertyEditorRegistry registry) { Map> input = new HashMap<>(); HashSet value1 = new HashSet<>(); - value1.add(new Integer(1)); + value1.add(1); input.put("1", value1); ArrayList value2 = new ArrayList<>(); value2.add(Boolean.TRUE); @@ -591,9 +590,9 @@ public void registerCustomEditors(PropertyEditorRegistry registry) { bf.registerBeanDefinition("genericBean", rbd); GenericBean gb = (GenericBean) bf.getBean("genericBean"); - boolean condition1 = gb.getCollectionMap().get(new Integer(1)) instanceof HashSet; + boolean condition1 = gb.getCollectionMap().get(1) instanceof HashSet; assertThat(condition1).isTrue(); - boolean condition = gb.getCollectionMap().get(new Integer(2)) instanceof ArrayList; + boolean condition = gb.getCollectionMap().get(2) instanceof ArrayList; assertThat(condition).isTrue(); } @@ -624,7 +623,7 @@ public void testGenericMapBean() throws Exception { new ClassPathResource("genericBeanTests.xml", getClass())); Map map = (Map) bf.getBean("map"); assertThat(map.size()).isEqualTo(1); - assertThat(map.keySet().iterator().next()).isEqualTo(new Integer(10)); + assertThat(map.keySet().iterator().next()).isEqualTo(10); assertThat(map.values().iterator().next()).isEqualTo(new URL("http://localhost:8080")); } @@ -634,9 +633,9 @@ public void testGenericallyTypedIntegerBean() { new XmlBeanDefinitionReader(bf).loadBeanDefinitions( new ClassPathResource("genericBeanTests.xml", getClass())); GenericIntegerBean gb = (GenericIntegerBean) bf.getBean("integerBean"); - assertThat(gb.getGenericProperty()).isEqualTo(new Integer(10)); - assertThat(gb.getGenericListProperty().get(0)).isEqualTo(new Integer(20)); - assertThat(gb.getGenericListProperty().get(1)).isEqualTo(new Integer(30)); + assertThat(gb.getGenericProperty()).isEqualTo(10); + assertThat(gb.getGenericListProperty().get(0)).isEqualTo(20); + assertThat(gb.getGenericListProperty().get(1)).isEqualTo(30); } @Test @@ -645,9 +644,9 @@ public void testGenericallyTypedSetOfIntegerBean() { new XmlBeanDefinitionReader(bf).loadBeanDefinitions( new ClassPathResource("genericBeanTests.xml", getClass())); GenericSetOfIntegerBean gb = (GenericSetOfIntegerBean) bf.getBean("setOfIntegerBean"); - assertThat(gb.getGenericProperty().iterator().next()).isEqualTo(new Integer(10)); - assertThat(gb.getGenericListProperty().get(0).iterator().next()).isEqualTo(new Integer(20)); - assertThat(gb.getGenericListProperty().get(1).iterator().next()).isEqualTo(new Integer(30)); + assertThat(gb.getGenericProperty().iterator().next()).isEqualTo(10); + assertThat(gb.getGenericListProperty().get(0).iterator().next()).isEqualTo(20); + assertThat(gb.getGenericListProperty().get(1).iterator().next()).isEqualTo(30); } @Test @@ -960,7 +959,7 @@ public void testGenericMatchingWithUnresolvedOrderedStream() { @SuppressWarnings("serial") - public static class NamedUrlList extends LinkedList { + public static class NamedUrlList extends ArrayList { } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistryTests.java index 303bc31ec4a9..6e3f8465e9e8 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistryTests.java @@ -20,8 +20,8 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.ObjectFactory; -import org.springframework.tests.sample.beans.DerivedTestBean; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.DerivedTestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/DefinitionMetadataEqualsHashCodeTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/DefinitionMetadataEqualsHashCodeTests.java index ca09f9ee8464..36c0c4be39cc 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/DefinitionMetadataEqualsHashCodeTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/DefinitionMetadataEqualsHashCodeTests.java @@ -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. @@ -20,7 +20,7 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.RuntimeBeanReference; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; @@ -126,7 +126,7 @@ private void assertEqualsAndHashCodeContracts(Object master, Object equal, Objec assertThat(equal.hashCode()).as("Hash code for equal instances should match").isEqualTo(master.hashCode()); assertThat(notEqual).as("Should not be equal").isNotEqualTo(master); - assertThat(notEqual.hashCode()).as("Hash code for non-equal instances should not match").isNotEqualTo((long) master.hashCode()); + assertThat(notEqual.hashCode()).as("Hash code for non-equal instances should not match").isNotEqualTo(master.hashCode()); assertThat(subclass).as("Subclass should be equal").isEqualTo(master); assertThat(subclass.hashCode()).as("Hash code for subclass should match").isEqualTo(master.hashCode()); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/LookupMethodTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/LookupMethodTests.java index a12a21772b77..eeb34b6f8f1e 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/LookupMethodTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/LookupMethodTests.java @@ -20,8 +20,8 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/PropertiesBeanDefinitionReaderTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/PropertiesBeanDefinitionReaderTests.java index dd811aa41239..d7df748c5c94 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/PropertiesBeanDefinitionReaderTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/PropertiesBeanDefinitionReaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,16 @@ import org.junit.jupiter.api.Test; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; /** * @author Rob Harrop */ -public class PropertiesBeanDefinitionReaderTests { +@SuppressWarnings("deprecation") +class PropertiesBeanDefinitionReaderTests { private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); @@ -34,14 +35,14 @@ public class PropertiesBeanDefinitionReaderTests { @Test - public void withSimpleConstructorArg() { + void withSimpleConstructorArg() { this.reader.loadBeanDefinitions(new ClassPathResource("simpleConstructorArg.properties", getClass())); TestBean bean = (TestBean) this.beanFactory.getBean("testBean"); assertThat(bean.getName()).isEqualTo("Rob Harrop"); } @Test - public void withConstructorArgRef() { + void withConstructorArgRef() { this.reader.loadBeanDefinitions(new ClassPathResource("refConstructorArg.properties", getClass())); TestBean rob = (TestBean) this.beanFactory.getBean("rob"); TestBean sally = (TestBean) this.beanFactory.getBean("sally"); @@ -49,7 +50,7 @@ public void withConstructorArgRef() { } @Test - public void withMultipleConstructorsArgs() { + void withMultipleConstructorsArgs() { this.reader.loadBeanDefinitions(new ClassPathResource("multiConstructorArgs.properties", getClass())); TestBean bean = (TestBean) this.beanFactory.getBean("testBean"); assertThat(bean.getName()).isEqualTo("Rob Harrop"); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/Spr8954Tests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/Spr8954Tests.java index aa463b93e2d2..a1db2e31356e 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/Spr8954Tests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/Spr8954Tests.java @@ -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. @@ -24,7 +24,7 @@ import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; -import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; +import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor; import static org.assertj.core.api.Assertions.assertThat; @@ -125,7 +125,7 @@ interface PredictedType { static class PredictedTypeImpl implements PredictedType { } - static class PredictingBPP extends InstantiationAwareBeanPostProcessorAdapter { + static class PredictingBPP implements SmartInstantiationAwareBeanPostProcessor { @Override public Class predictBeanType(Class beanClass, String beanName) { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/security/CallbacksSecurityTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/security/CallbacksSecurityTests.java index 8561abef9f87..368e0b664725 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/security/CallbacksSecurityTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/security/CallbacksSecurityTests.java @@ -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. @@ -45,7 +45,7 @@ import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.SmartFactoryBean; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.SecurityContextProvider; @@ -55,6 +55,7 @@ import org.springframework.core.NestedRuntimeException; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; +import org.springframework.core.testfixture.security.TestPrincipal; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -252,37 +253,6 @@ public String run() { }); } - private static class TestPrincipal implements Principal { - - private String name; - - public TestPrincipal(String name) { - this.name = name; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof TestPrincipal)) { - return false; - } - TestPrincipal p = (TestPrincipal) obj; - return this.name.equals(p.name); - } - - @Override - public int hashCode() { - return this.name.hashCode(); - } - } - public CallbacksSecurityTests() { // setup security if (System.getSecurityManager() == null) { @@ -390,14 +360,14 @@ public void testCustomFactoryType() throws Exception { public void testCustomStaticFactoryMethod() throws Exception { assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> beanFactory.getBean("custom-static-factory-method")) - .satisfies(ex -> assertThat(ex.getMostSpecificCause()).isInstanceOf(SecurityException.class)); + .satisfies(mostSpecificCauseOf(SecurityException.class)); } @Test public void testCustomInstanceFactoryMethod() throws Exception { assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> beanFactory.getBean("custom-factory-method")) - .satisfies(ex -> assertThat(ex.getMostSpecificCause()).isInstanceOf(SecurityException.class)); + .satisfies(mostSpecificCauseOf(SecurityException.class)); } @Test @@ -442,7 +412,7 @@ public void testInitSecurityAwarePrototypeBean() { final DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); BeanDefinitionBuilder bdb = BeanDefinitionBuilder .genericBeanDefinition(NonPrivilegedBean.class).setScope( - ConfigurableBeanFactory.SCOPE_PROTOTYPE) + BeanDefinition.SCOPE_PROTOTYPE) .setInitMethodName("init").setDestroyMethodName("destroy") .addConstructorArgValue("user1"); lbf.registerBeanDefinition("test", bdb.getBeanDefinition()); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/wiring/BeanConfigurerSupportTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/wiring/BeanConfigurerSupportTests.java index 8c5cb1e8b5c6..8ff2952fb113 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/wiring/BeanConfigurerSupportTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/wiring/BeanConfigurerSupportTests.java @@ -21,7 +21,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/AutowireWithExclusionTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/AutowireWithExclusionTests.java index 2ed794e57470..8ea0719a82be 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/AutowireWithExclusionTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/AutowireWithExclusionTests.java @@ -22,8 +22,8 @@ import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/CollectionMergingTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/CollectionMergingTests.java index 720bf605f38f..04b82ca200a8 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/CollectionMergingTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/CollectionMergingTests.java @@ -27,8 +27,8 @@ import org.springframework.beans.factory.support.BeanDefinitionReader; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/CollectionsWithDefaultTypesTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/CollectionsWithDefaultTypesTests.java index 1cdd84ba9fad..8a67fea2c1ba 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/CollectionsWithDefaultTypesTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/CollectionsWithDefaultTypesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,8 +22,8 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; @@ -85,8 +85,8 @@ public void testBuildCollectionFromMixtureOfReferencesAndValues() throws Excepti List l = (List) jumble.getJumble(); assertThat(l.get(0).equals("literal")).isTrue(); Integer[] array1 = (Integer[]) l.get(1); - assertThat(array1[0].equals(new Integer(2))).isTrue(); - assertThat(array1[1].equals(new Integer(4))).isTrue(); + assertThat(array1[0].equals(2)).isTrue(); + assertThat(array1[1].equals(4)).isTrue(); int[] array2 = (int[]) l.get(2); assertThat(array2[0] == 3).isTrue(); assertThat(array2[1] == 5).isTrue(); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/ConstructorDependenciesBean.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/ConstructorDependenciesBean.java index f4ca864f830e..1d3c6286d312 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/ConstructorDependenciesBean.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/ConstructorDependenciesBean.java @@ -18,8 +18,8 @@ import java.io.Serializable; -import org.springframework.tests.sample.beans.IndexedTestBean; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.IndexedTestBean; +import org.springframework.beans.testfixture.beans.TestBean; /** * Simple bean used to check constructor dependency checking. diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/CountingFactory.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/CountingFactory.java index 3a992e3334ba..21ccf619b38b 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/CountingFactory.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/CountingFactory.java @@ -17,7 +17,7 @@ package org.springframework.beans.factory.xml; import org.springframework.beans.factory.FactoryBean; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; /** * @author Juergen Hoeller diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/DummyReferencer.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/DummyReferencer.java index 352e08958048..10bf54f5e07c 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/DummyReferencer.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/DummyReferencer.java @@ -17,8 +17,8 @@ package org.springframework.beans.factory.xml; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.tests.sample.beans.factory.DummyFactory; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.beans.testfixture.beans.factory.DummyFactory; /** * @author Juergen Hoeller diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/DuplicateBeanIdTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/DuplicateBeanIdTests.java index 983292c56aad..c52001463cdf 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/DuplicateBeanIdTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/DuplicateBeanIdTests.java @@ -19,8 +19,8 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -37,8 +37,8 @@ * * @author Chris Beams * @since 3.1 - * @see org.springframework.beans.factory.xml.XmlBeanFactoryTests#testWithDuplicateName - * @see org.springframework.beans.factory.xml.XmlBeanFactoryTests#testWithDuplicateNameInAlias + * @see org.springframework.beans.factory.xml.XmlBeanFactoryTests#withDuplicateName + * @see org.springframework.beans.factory.xml.XmlBeanFactoryTests#withDuplicateNameInAlias */ public class DuplicateBeanIdTests { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/EventPublicationTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/EventPublicationTests.java index 7fef2df708e3..29e2561fc756 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/EventPublicationTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/EventPublicationTests.java @@ -30,8 +30,8 @@ import org.springframework.beans.factory.parsing.ImportDefinition; import org.springframework.beans.factory.parsing.PassThroughSourceExtractor; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.testfixture.beans.CollectingReaderEventListener; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.beans.CollectingReaderEventListener; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/FactoryMethodTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/FactoryMethodTests.java index 3985567eb54c..d2f26ee0fe63 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/FactoryMethodTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/FactoryMethodTests.java @@ -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. @@ -25,8 +25,9 @@ import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.testfixture.beans.FactoryMethods; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -287,7 +288,7 @@ public void testCanSpecifyFactoryMethodArgumentsOnFactoryMethodPrototype() { assertThat(fm2.getTestBean()).isSameAs(fm2.getTestBean()); assertThat(fm2).isNotSameAs(fm1); - FactoryMethods fm3 = (FactoryMethods) xbf.getBean("testBeanOnlyPrototype", tbArg2, new Integer(1), "myName"); + FactoryMethods fm3 = (FactoryMethods) xbf.getBean("testBeanOnlyPrototype", tbArg2, 1, "myName"); assertThat(fm3.getNum()).isEqualTo(1); assertThat(fm3.getName()).isEqualTo("myName"); assertThat(fm3.getTestBean().getName()).isEqualTo("arg2"); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/InstanceFactory.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/InstanceFactory.java index 8cf0fa91099c..af7f2f718b19 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/InstanceFactory.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/InstanceFactory.java @@ -16,7 +16,8 @@ package org.springframework.beans.factory.xml; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.FactoryMethods; +import org.springframework.beans.testfixture.beans.TestBean; /** * Test class for Spring's ability to create objects using diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/NestedBeansElementAttributeRecursionTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/NestedBeansElementAttributeRecursionTests.java index 6934e5166bc2..cce3bb3ca15b 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/NestedBeansElementAttributeRecursionTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/NestedBeansElementAttributeRecursionTests.java @@ -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. @@ -20,13 +20,11 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; - - /** * Tests for propagating enclosing beans element defaults to nested beans elements. * @@ -69,7 +67,6 @@ private void assertLazyInits(DefaultListableBeanFactory bf) { } @Test - @SuppressWarnings("unchecked") public void defaultMerge() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(bf).loadBeanDefinitions( @@ -79,7 +76,6 @@ public void defaultMerge() { } @Test - @SuppressWarnings("unchecked") public void defaultMergeWithNonValidatingParser() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(bf); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/SchemaValidationTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/SchemaValidationTests.java index 1d51dc3f0bb1..70199450995b 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/SchemaValidationTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/SchemaValidationTests.java @@ -21,8 +21,8 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/SimpleConstructorNamespaceHandlerTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/SimpleConstructorNamespaceHandlerTests.java index 0d4921e4138d..24a9d43e89be 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/SimpleConstructorNamespaceHandlerTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/SimpleConstructorNamespaceHandlerTests.java @@ -20,9 +20,9 @@ import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.testfixture.beans.DummyBean; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.sample.beans.DummyBean; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/SimplePropertyNamespaceHandlerTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/SimplePropertyNamespaceHandlerTests.java index b8f943fccd22..68ef31d9a82f 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/SimplePropertyNamespaceHandlerTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/SimplePropertyNamespaceHandlerTests.java @@ -20,9 +20,9 @@ import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/TestBeanCreator.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/TestBeanCreator.java index adee655ad494..091a20290e7c 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/TestBeanCreator.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/TestBeanCreator.java @@ -16,7 +16,7 @@ package org.springframework.beans.factory.xml; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; /** * Test class for Spring's ability to create diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/UtilNamespaceHandlerTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/UtilNamespaceHandlerTests.java index be6be77551c5..2260a86c52e9 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/UtilNamespaceHandlerTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/UtilNamespaceHandlerTests.java @@ -32,10 +32,10 @@ import org.springframework.beans.factory.parsing.ComponentDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.testfixture.beans.CollectingReaderEventListener; +import org.springframework.beans.testfixture.beans.CustomEnum; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.beans.CollectingReaderEventListener; -import org.springframework.tests.sample.beans.CustomEnum; -import org.springframework.tests.sample.beans.TestBean; import org.springframework.util.LinkedCaseInsensitiveMap; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlBeanCollectionTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlBeanCollectionTests.java index 17eadf0c61dc..e44f0d9df7a0 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlBeanCollectionTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlBeanCollectionTests.java @@ -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. @@ -38,9 +38,9 @@ import org.springframework.beans.factory.config.MapFactoryBean; import org.springframework.beans.factory.config.SetFactoryBean; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.testfixture.beans.HasMap; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.sample.beans.HasMap; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -201,16 +201,16 @@ public void testMapWithLiteralsOnly() throws Exception { public void testMapWithLiteralsAndReferences() throws Exception { HasMap hasMap = (HasMap) this.beanFactory.getBean("mixedMap"); assertThat(hasMap.getMap().size() == 5).isTrue(); - assertThat(hasMap.getMap().get("foo").equals(new Integer(10))).isTrue(); + assertThat(hasMap.getMap().get("foo").equals(10)).isTrue(); TestBean jenny = (TestBean) this.beanFactory.getBean("jenny"); assertThat(hasMap.getMap().get("jenny") == jenny).isTrue(); - assertThat(hasMap.getMap().get(new Integer(5)).equals("david")).isTrue(); + assertThat(hasMap.getMap().get(5).equals("david")).isTrue(); boolean condition1 = hasMap.getMap().get("bar") instanceof Long; assertThat(condition1).isTrue(); - assertThat(hasMap.getMap().get("bar").equals(new Long(100))).isTrue(); + assertThat(hasMap.getMap().get("bar").equals(100L)).isTrue(); boolean condition = hasMap.getMap().get("baz") instanceof Integer; assertThat(condition).isTrue(); - assertThat(hasMap.getMap().get("baz").equals(new Integer(200))).isTrue(); + assertThat(hasMap.getMap().get("baz").equals(200)).isTrue(); } @Test diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReaderTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReaderTests.java index 9b460c6601af..69768da8278f 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReaderTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlBeanDefinitionReaderTests.java @@ -25,10 +25,10 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.Resource; -import org.springframework.tests.sample.beans.TestBean; import org.springframework.util.ObjectUtils; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlListableBeanFactoryTests.java index d0b83d98155a..d48b55f2cb8d 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlListableBeanFactoryTests.java @@ -30,11 +30,12 @@ import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.LifecycleBean; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.beans.testfixture.beans.factory.DummyFactory; +import org.springframework.beans.testfixture.factory.xml.AbstractListableBeanFactoryTests; import org.springframework.core.io.ClassPathResource; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.LifecycleBean; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.tests.sample.beans.factory.DummyFactory; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/BeanInfoTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/BeanInfoTests.java index b08587fa6ea5..7b91fc60e64a 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/BeanInfoTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/BeanInfoTests.java @@ -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. @@ -39,12 +39,12 @@ public class BeanInfoTests { public void testComplexObject() { ValueBean bean = new ValueBean(); BeanWrapper bw = new BeanWrapperImpl(bean); - Integer value = new Integer(1); + Integer value = 1; bw.setPropertyValue("value", value); assertThat(value).as("value not set correctly").isEqualTo(bean.getValue()); - value = new Integer(2); + value = 2; bw.setPropertyValue("value", value.toString()); assertThat(value).as("value not converted").isEqualTo(bean.getValue()); diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/CustomCollectionEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/CustomCollectionEditorTests.java index 36fd15b997dd..ad8d4c761de2 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/CustomCollectionEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/CustomCollectionEditorTests.java @@ -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. @@ -63,9 +63,9 @@ public void testSunnyDaySetValue() throws Exception { assertThat(condition).isTrue(); List list = (List) value; assertThat(list.size()).as("There must be 3 elements in the converted collection").isEqualTo(3); - assertThat(list.get(0)).isEqualTo(new Integer(0)); - assertThat(list.get(1)).isEqualTo(new Integer(1)); - assertThat(list.get(2)).isEqualTo(new Integer(2)); + assertThat(list.get(0)).isEqualTo(0); + assertThat(list.get(1)).isEqualTo(1); + assertThat(list.get(2)).isEqualTo(2); } @Test diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/CustomEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/CustomEditorTests.java index 85bf11b9d619..0de98a10f002 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/CustomEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/CustomEditorTests.java @@ -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. @@ -43,11 +43,11 @@ import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValue; -import org.springframework.tests.sample.beans.BooleanTestBean; -import org.springframework.tests.sample.beans.ITestBean; -import org.springframework.tests.sample.beans.IndexedTestBean; -import org.springframework.tests.sample.beans.NumberTestBean; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.BooleanTestBean; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.IndexedTestBean; +import org.springframework.beans.testfixture.beans.NumberTestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -75,7 +75,7 @@ public void testComplexObject() { BeanWrapper bw = new BeanWrapperImpl(tb); bw.registerCustomEditor(ITestBean.class, new TestBeanEditor()); MutablePropertyValues pvs = new MutablePropertyValues(); - pvs.addPropertyValue(new PropertyValue("age", new Integer(55))); + pvs.addPropertyValue(new PropertyValue("age", 55)); pvs.addPropertyValue(new PropertyValue("name", newName)); pvs.addPropertyValue(new PropertyValue("touchy", "valid")); pvs.addPropertyValue(new PropertyValue("spouse", tbString)); @@ -94,7 +94,7 @@ public void testComplexObjectWithOldValueAccess() { bw.setExtractOldValueForEditor(true); bw.registerCustomEditor(ITestBean.class, new OldValueAccessingTestBeanEditor()); MutablePropertyValues pvs = new MutablePropertyValues(); - pvs.addPropertyValue(new PropertyValue("age", new Integer(55))); + pvs.addPropertyValue(new PropertyValue("age", 55)); pvs.addPropertyValue(new PropertyValue("name", newName)); pvs.addPropertyValue(new PropertyValue("touchy", "valid")); pvs.addPropertyValue(new PropertyValue("spouse", tbString)); @@ -593,9 +593,9 @@ public void testClassEditorWithNonExistentClass() throws Exception { @Test public void testClassEditorWithArray() { PropertyEditor classEditor = new ClassEditor(); - classEditor.setAsText("org.springframework.tests.sample.beans.TestBean[]"); + classEditor.setAsText("org.springframework.beans.testfixture.beans.TestBean[]"); assertThat(classEditor.getValue()).isEqualTo(TestBean[].class); - assertThat(classEditor.getAsText()).isEqualTo("org.springframework.tests.sample.beans.TestBean[]"); + assertThat(classEditor.getAsText()).isEqualTo("org.springframework.beans.testfixture.beans.TestBean[]"); } /* @@ -756,7 +756,7 @@ public void testCustomDateEditorWithExactDateLength() { public void testCustomNumberEditor() { CustomNumberEditor editor = new CustomNumberEditor(Integer.class, false); editor.setAsText("5"); - assertThat(editor.getValue()).isEqualTo(new Integer(5)); + assertThat(editor.getValue()).isEqualTo(5); assertThat(editor.getAsText()).isEqualTo("5"); editor.setValue(null); assertThat(editor.getValue()).isEqualTo(null); @@ -767,14 +767,14 @@ public void testCustomNumberEditor() { public void testCustomNumberEditorWithHex() { CustomNumberEditor editor = new CustomNumberEditor(Integer.class, false); editor.setAsText("0x" + Integer.toHexString(64)); - assertThat(editor.getValue()).isEqualTo(new Integer(64)); + assertThat(editor.getValue()).isEqualTo(64); } @Test public void testCustomNumberEditorWithEmptyAsNull() { CustomNumberEditor editor = new CustomNumberEditor(Integer.class, true); editor.setAsText("5"); - assertThat(editor.getValue()).isEqualTo(new Integer(5)); + assertThat(editor.getValue()).isEqualTo(5); assertThat(editor.getAsText()).isEqualTo("5"); editor.setAsText(""); assertThat(editor.getValue()).isEqualTo(null); diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/PathEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/PathEditorTests.java index 40354fc44643..f0c659bcbdb7 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/PathEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/PathEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,57 +34,78 @@ public class PathEditorTests { @Test - public void testClasspathPathName() throws Exception { + public void testClasspathPathName() { PropertyEditor pathEditor = new PathEditor(); pathEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) + "/" + ClassUtils.getShortName(getClass()) + ".class"); Object value = pathEditor.getValue(); - boolean condition = value instanceof Path; - assertThat(condition).isTrue(); + assertThat(value instanceof Path).isTrue(); Path path = (Path) value; assertThat(path.toFile().exists()).isTrue(); } @Test - public void testWithNonExistentResource() throws Exception { + public void testWithNonExistentResource() { PropertyEditor propertyEditor = new PathEditor(); assertThatIllegalArgumentException().isThrownBy(() -> propertyEditor.setAsText("classpath:/no_way_this_file_is_found.doc")); } @Test - public void testWithNonExistentPath() throws Exception { + public void testWithNonExistentPath() { PropertyEditor pathEditor = new PathEditor(); pathEditor.setAsText("file:/no_way_this_file_is_found.doc"); Object value = pathEditor.getValue(); - boolean condition1 = value instanceof Path; - assertThat(condition1).isTrue(); + assertThat(value instanceof Path).isTrue(); Path path = (Path) value; - boolean condition = !path.toFile().exists(); - assertThat(condition).isTrue(); + assertThat(!path.toFile().exists()).isTrue(); } @Test - public void testAbsolutePath() throws Exception { + public void testAbsolutePath() { PropertyEditor pathEditor = new PathEditor(); pathEditor.setAsText("/no_way_this_file_is_found.doc"); Object value = pathEditor.getValue(); - boolean condition1 = value instanceof Path; - assertThat(condition1).isTrue(); + assertThat(value instanceof Path).isTrue(); Path path = (Path) value; - boolean condition = !path.toFile().exists(); - assertThat(condition).isTrue(); + assertThat(!path.toFile().exists()).isTrue(); } @Test - public void testUnqualifiedPathNameFound() throws Exception { + public void testWindowsAbsolutePath() { + PropertyEditor pathEditor = new PathEditor(); + pathEditor.setAsText("C:\\no_way_this_file_is_found.doc"); + Object value = pathEditor.getValue(); + assertThat(value instanceof Path).isTrue(); + Path path = (Path) value; + assertThat(!path.toFile().exists()).isTrue(); + } + + @Test + public void testWindowsAbsoluteFilePath() { + PropertyEditor pathEditor = new PathEditor(); + try { + pathEditor.setAsText("file://C:\\no_way_this_file_is_found.doc"); + Object value = pathEditor.getValue(); + assertThat(value instanceof Path).isTrue(); + Path path = (Path) value; + assertThat(!path.toFile().exists()).isTrue(); + } + catch (IllegalArgumentException ex) { + if (File.separatorChar == '\\') { // on Windows, otherwise silently ignore + throw ex; + } + } + } + + @Test + public void testUnqualifiedPathNameFound() { PropertyEditor pathEditor = new PathEditor(); String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" + ClassUtils.getShortName(getClass()) + ".class"; pathEditor.setAsText(fileName); Object value = pathEditor.getValue(); - boolean condition = value instanceof Path; - assertThat(condition).isTrue(); + assertThat(value instanceof Path).isTrue(); Path path = (Path) value; File file = path.toFile(); assertThat(file.exists()).isTrue(); @@ -96,14 +117,13 @@ public void testUnqualifiedPathNameFound() throws Exception { } @Test - public void testUnqualifiedPathNameNotFound() throws Exception { + public void testUnqualifiedPathNameNotFound() { PropertyEditor pathEditor = new PathEditor(); String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" + ClassUtils.getShortName(getClass()) + ".clazz"; pathEditor.setAsText(fileName); Object value = pathEditor.getValue(); - boolean condition = value instanceof Path; - assertThat(condition).isTrue(); + assertThat(value instanceof Path).isTrue(); Path path = (Path) value; File file = path.toFile(); assertThat(file.exists()).isFalse(); diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/StringArrayPropertyEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/StringArrayPropertyEditorTests.java index 09716d04fd08..0583c78460b7 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/StringArrayPropertyEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/StringArrayPropertyEditorTests.java @@ -23,39 +23,31 @@ /** * @author Rick Evans * @author Juergen Hoeller + * @author Sam Brannen */ -public class StringArrayPropertyEditorTests { +class StringArrayPropertyEditorTests { @Test - public void withDefaultSeparator() throws Exception { + void withDefaultSeparator() { StringArrayPropertyEditor editor = new StringArrayPropertyEditor(); editor.setAsText("0,1,2"); Object value = editor.getValue(); - assertThat(value).isNotNull(); - boolean condition = value instanceof String[]; - assertThat(condition).isTrue(); - String[] array = (String[]) value; - for (int i = 0; i < array.length; ++i) { - assertThat(array[i]).isEqualTo(("" + i)); - } + assertTrimmedElements(value); assertThat(editor.getAsText()).isEqualTo("0,1,2"); } @Test - public void trimByDefault() throws Exception { + void trimByDefault() { StringArrayPropertyEditor editor = new StringArrayPropertyEditor(); editor.setAsText(" 0,1 , 2 "); Object value = editor.getValue(); - String[] array = (String[]) value; - for (int i = 0; i < array.length; ++i) { - assertThat(array[i]).isEqualTo(("" + i)); - } + assertTrimmedElements(value); assertThat(editor.getAsText()).isEqualTo("0,1,2"); } @Test - public void noTrim() throws Exception { - StringArrayPropertyEditor editor = new StringArrayPropertyEditor(",",false,false); + void noTrim() { + StringArrayPropertyEditor editor = new StringArrayPropertyEditor(",", false, false); editor.setAsText(" 0,1 , 2 "); Object value = editor.getValue(); String[] array = (String[]) value; @@ -67,48 +59,45 @@ public void noTrim() throws Exception { } @Test - public void withCustomSeparator() throws Exception { + void withCustomSeparator() { StringArrayPropertyEditor editor = new StringArrayPropertyEditor(":"); editor.setAsText("0:1:2"); Object value = editor.getValue(); - boolean condition = value instanceof String[]; - assertThat(condition).isTrue(); - String[] array = (String[]) value; - for (int i = 0; i < array.length; ++i) { - assertThat(array[i]).isEqualTo(("" + i)); - } + assertTrimmedElements(value); assertThat(editor.getAsText()).isEqualTo("0:1:2"); } @Test - public void withCharsToDelete() throws Exception { + void withCharsToDelete() { StringArrayPropertyEditor editor = new StringArrayPropertyEditor(",", "\r\n", false); editor.setAsText("0\r,1,\n2"); Object value = editor.getValue(); - boolean condition = value instanceof String[]; - assertThat(condition).isTrue(); - String[] array = (String[]) value; - for (int i = 0; i < array.length; ++i) { - assertThat(array[i]).isEqualTo(("" + i)); - } + assertTrimmedElements(value); assertThat(editor.getAsText()).isEqualTo("0,1,2"); } @Test - public void withEmptyArray() throws Exception { + void withEmptyArray() { StringArrayPropertyEditor editor = new StringArrayPropertyEditor(); editor.setAsText(""); Object value = editor.getValue(); - boolean condition = value instanceof String[]; - assertThat(condition).isTrue(); - assertThat(((String[]) value).length).isEqualTo(0); + assertThat(value).isInstanceOf(String[].class); + assertThat((String[]) value).isEmpty(); } @Test - public void withEmptyArrayAsNull() throws Exception { + void withEmptyArrayAsNull() { StringArrayPropertyEditor editor = new StringArrayPropertyEditor(",", true); editor.setAsText(""); assertThat(editor.getValue()).isNull(); } + private static void assertTrimmedElements(Object value) { + assertThat(value).isInstanceOf(String[].class); + String[] array = (String[]) value; + for (int i = 0; i < array.length; ++i) { + assertThat(array[i]).isEqualTo(("" + i)); + } + } + } diff --git a/spring-beans/src/test/java/org/springframework/beans/support/PagedListHolderTests.java b/spring-beans/src/test/java/org/springframework/beans/support/PagedListHolderTests.java index 74a8769d7044..0afce6483444 100644 --- a/spring-beans/src/test/java/org/springframework/beans/support/PagedListHolderTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/support/PagedListHolderTests.java @@ -21,7 +21,7 @@ import org.junit.jupiter.api.Test; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-beans/src/test/kotlin/org/springframework/beans/KotlinBeanUtilsTests.kt b/spring-beans/src/test/kotlin/org/springframework/beans/KotlinBeanUtilsTests.kt index 948292bd8386..647d320fad96 100644 --- a/spring-beans/src/test/kotlin/org/springframework/beans/KotlinBeanUtilsTests.kt +++ b/spring-beans/src/test/kotlin/org/springframework/beans/KotlinBeanUtilsTests.kt @@ -74,6 +74,22 @@ class KotlinBeanUtilsTests { assertThat(baz.param2).isEqualTo(12) } + @Test + @Suppress("UsePropertyAccessSyntax") + fun `Instantiate class with private constructor`() { + BeanUtils.instantiateClass(PrivateConstructor::class.java.getDeclaredConstructor()) + } + + @Test + fun `Instantiate class with protected constructor`() { + BeanUtils.instantiateClass(ProtectedConstructor::class.java.getDeclaredConstructor()) + } + + @Test + fun `Instantiate private class`() { + BeanUtils.instantiateClass(PrivateClass::class.java.getDeclaredConstructor()) + } + class Foo(val param1: String, val param2: Int) class Bar(val param1: String, val param2: Int = 12) @@ -106,4 +122,10 @@ class KotlinBeanUtilsTests { constructor(param1: String) } + class PrivateConstructor private constructor() + + open class ProtectedConstructor protected constructor() + + private class PrivateClass + } diff --git a/spring-beans/src/test/kotlin/org/springframework/beans/factory/annotation/KotlinAutowiredTests.kt b/spring-beans/src/test/kotlin/org/springframework/beans/factory/annotation/KotlinAutowiredTests.kt index a254900fa36b..37d6793cad50 100644 --- a/spring-beans/src/test/kotlin/org/springframework/beans/factory/annotation/KotlinAutowiredTests.kt +++ b/spring-beans/src/test/kotlin/org/springframework/beans/factory/annotation/KotlinAutowiredTests.kt @@ -22,8 +22,8 @@ import org.junit.jupiter.api.Test import org.springframework.beans.factory.BeanCreationException import org.springframework.beans.factory.support.DefaultListableBeanFactory import org.springframework.beans.factory.support.RootBeanDefinition -import org.springframework.tests.sample.beans.Colour -import org.springframework.tests.sample.beans.TestBean +import org.springframework.beans.testfixture.beans.Colour +import org.springframework.beans.testfixture.beans.TestBean /** * Tests for Kotlin support with [Autowired]. diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/BeanFactoryUtilsTests-leaf.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/BeanFactoryUtilsTests-leaf.xml index e9abc664d662..435dcb06cbd8 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/BeanFactoryUtilsTests-leaf.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/BeanFactoryUtilsTests-leaf.xml @@ -3,7 +3,7 @@ - + custom 25 diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/BeanFactoryUtilsTests-middle.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/BeanFactoryUtilsTests-middle.xml index 4807313fed67..4804e44cf889 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/BeanFactoryUtilsTests-middle.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/BeanFactoryUtilsTests-middle.xml @@ -4,7 +4,7 @@ - + custom 666 @@ -13,7 +13,7 @@ Check that invoker is automatically added to wrap target. Non pointcut bean name should be wrapped in invoker. --> - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/BeanFactoryUtilsTests-root.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/BeanFactoryUtilsTests-root.xml index 26560b27fb4c..fea62a3791b1 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/BeanFactoryUtilsTests-root.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/BeanFactoryUtilsTests-root.xml @@ -6,21 +6,21 @@ - + - + - + - + custom 25 - + - + false diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/config/FieldRetrievingFactoryBeanTests-context.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/config/FieldRetrievingFactoryBeanTests-context.xml index c14569c7fa57..35c854bdb0e0 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/config/FieldRetrievingFactoryBeanTests-context.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/config/FieldRetrievingFactoryBeanTests-context.xml @@ -3,7 +3,7 @@ - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/config/PropertyPathFactoryBeanTests-context.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/config/PropertyPathFactoryBeanTests-context.xml index dbcea8f953b0..d9979e38339a 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/config/PropertyPathFactoryBeanTests-context.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/config/PropertyPathFactoryBeanTests-context.xml @@ -3,19 +3,19 @@ - + 10 - + 11 - + 98 - + 99 @@ -23,7 +23,7 @@ - + 12 @@ -46,10 +46,10 @@ tb spouse - org.springframework.tests.sample.beans.TestBean + org.springframework.beans.testfixture.beans.TestBean - + @@ -59,11 +59,11 @@ - + - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/config/SimpleScopeTests-context.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/config/SimpleScopeTests-context.xml index 5a180e701381..9ac321b0cc45 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/config/SimpleScopeTests-context.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/config/SimpleScopeTests-context.xml @@ -3,6 +3,6 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/parsing/CustomProblemReporterTests-context.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/parsing/CustomProblemReporterTests-context.xml index 12b096820436..1c6e22b51182 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/parsing/CustomProblemReporterTests-context.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/parsing/CustomProblemReporterTests-context.xml @@ -6,7 +6,7 @@ - + @@ -14,9 +14,9 @@ - + - + @@ -26,5 +26,6 @@ - + + \ No newline at end of file diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/support/genericBeanTests.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/support/genericBeanTests.xml index c8a0ab7549d7..c610f9cf1591 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/support/genericBeanTests.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/support/genericBeanTests.xml @@ -3,7 +3,7 @@ - + @@ -43,7 +43,7 @@ autowire="constructor"> - + @@ -53,7 +53,7 @@ - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/support/lookupMethodTests.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/support/lookupMethodTests.xml index 1334415c7460..a04f0e3beb42 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/support/lookupMethodTests.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/support/lookupMethodTests.xml @@ -13,9 +13,9 @@ - + - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/support/multiConstructorArgs.properties b/spring-beans/src/test/resources/org/springframework/beans/factory/support/multiConstructorArgs.properties index 8d5f74fc31a9..9cc82ac44933 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/support/multiConstructorArgs.properties +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/support/multiConstructorArgs.properties @@ -1,3 +1,3 @@ -testBean.(class)=org.springframework.tests.sample.beans.TestBean +testBean.(class)=org.springframework.beans.testfixture.beans.TestBean testBean.$0=Rob Harrop testBean.$1=23 diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/support/refConstructorArg.properties b/spring-beans/src/test/resources/org/springframework/beans/factory/support/refConstructorArg.properties index 4d3723c7de8a..b1bb01e2a507 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/support/refConstructorArg.properties +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/support/refConstructorArg.properties @@ -1,5 +1,5 @@ -sally.(class)=org.springframework.tests.sample.beans.TestBean +sally.(class)=org.springframework.beans.testfixture.beans.TestBean sally.name=Sally -rob.(class)=org.springframework.tests.sample.beans.TestBean +rob.(class)=org.springframework.beans.testfixture.beans.TestBean rob.$0(ref)=sally diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/support/simpleConstructorArg.properties b/spring-beans/src/test/resources/org/springframework/beans/factory/support/simpleConstructorArg.properties index d0f1eea32665..bc39b1b018c0 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/support/simpleConstructorArg.properties +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/support/simpleConstructorArg.properties @@ -1,2 +1,2 @@ -testBean.(class)=org.springframework.tests.sample.beans.TestBean +testBean.(class)=org.springframework.beans.testfixture.beans.TestBean testBean.$0=Rob Harrop diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/DuplicateBeanIdTests-multiLevel-context.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/DuplicateBeanIdTests-multiLevel-context.xml index f5f975bf7b51..0e326790f7fd 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/DuplicateBeanIdTests-multiLevel-context.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/DuplicateBeanIdTests-multiLevel-context.xml @@ -4,12 +4,12 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> - + - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/DuplicateBeanIdTests-sameLevel-context.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/DuplicateBeanIdTests-sameLevel-context.xml index 7bd11a987175..40ecbec4ac9a 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/DuplicateBeanIdTests-sameLevel-context.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/DuplicateBeanIdTests-sameLevel-context.xml @@ -4,12 +4,12 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> - + - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/NestedBeansElementAttributeRecursionTests-merge-context.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/NestedBeansElementAttributeRecursionTests-merge-context.xml index 55a5cf04e7fe..f3453c7bb943 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/NestedBeansElementAttributeRecursionTests-merge-context.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/NestedBeansElementAttributeRecursionTests-merge-context.xml @@ -5,7 +5,7 @@ https://www.springframework.org/schema/beans/spring-beans-3.1.xsd" default-merge="false"> - + alpha diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-constructor-with-exclusion.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-constructor-with-exclusion.xml index 0b67be5d8748..2b2515103095 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-constructor-with-exclusion.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-constructor-with-exclusion.xml @@ -3,9 +3,9 @@ - + - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-with-exclusion.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-with-exclusion.xml index 37a98d0ce810..692cc7743f56 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-with-exclusion.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-with-exclusion.xml @@ -3,9 +3,9 @@ - + - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-with-inclusion.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-with-inclusion.xml index b47d4231ce0c..f4090d14a71e 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-with-inclusion.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-with-inclusion.xml @@ -4,9 +4,9 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.5.xsd" default-autowire-candidates=""> - + - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-with-selective-inclusion.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-with-selective-inclusion.xml index 5df4f9c21dca..d52ba19ec021 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-with-selective-inclusion.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/autowire-with-selective-inclusion.xml @@ -4,9 +4,9 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.5.xsd" default-autowire-candidates="props*,*ly"> - + - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/beanEvents.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/beanEvents.xml index 9b019ee2ff2a..2bdea186844c 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/beanEvents.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/beanEvents.xml @@ -10,22 +10,22 @@ - + - + - + - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collectionMerging.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collectionMerging.xml index b3a6142b54fa..d45764707947 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collectionMerging.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collectionMerging.xml @@ -3,7 +3,7 @@ - + Rob Harrop @@ -23,12 +23,12 @@ - + - + Rob Harrop @@ -47,14 +47,14 @@ - + - + @@ -76,7 +76,7 @@ - + @@ -84,7 +84,7 @@ - + Sall @@ -103,7 +103,7 @@ - + Rob Harrop @@ -123,12 +123,12 @@ - + - + Rob Harrop @@ -147,14 +147,14 @@ - + - + @@ -176,7 +176,7 @@ - + @@ -184,7 +184,7 @@ - + Sall diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collections.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collections.xml index 0f2fff8c9b91..5683b4fe490e 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collections.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collections.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> - + Jenny 30 @@ -12,7 +12,7 @@ - + Simple bean, without any collections. @@ -23,7 +23,7 @@ 27 - + Rod 32 @@ -35,7 +35,7 @@ - + Jenny 30 @@ -43,13 +43,13 @@ - + David 27 - + Rod 32 @@ -63,7 +63,7 @@ - + loner 26 @@ -101,26 +101,26 @@ - + verbose - + - + - + - + @@ -130,7 +130,7 @@ - + @@ -157,7 +157,7 @@ - + @@ -176,7 +176,7 @@ - + @@ -185,7 +185,7 @@ - + @@ -217,14 +217,14 @@ - + - + bar @@ -234,7 +234,7 @@ - + bar @@ -244,7 +244,7 @@ - + @@ -253,7 +253,7 @@ - + @@ -261,7 +261,7 @@ - + bar @@ -270,7 +270,7 @@ - + @@ -279,7 +279,7 @@ - + one @@ -288,7 +288,7 @@ - + 0 @@ -298,11 +298,11 @@ - + - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collectionsWithDefaultTypes.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collectionsWithDefaultTypes.xml index 92d5d3db1dbd..a1a1ea378b1b 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collectionsWithDefaultTypes.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collectionsWithDefaultTypes.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> - + 1 @@ -28,7 +28,7 @@ - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/factory-methods.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/factory-methods.xml index 86d94732956d..93f338b00178 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/factory-methods.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/factory-methods.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> - setterString @@ -21,48 +21,48 @@ - - - setterString - 27 gotcha - 27 - 27 - 27 - @@ -72,31 +72,31 @@ - setterString - testBeanOnlyPrototypeDISetterString - - 27 gotcha - 27 @@ -104,10 +104,10 @@ bogus - - + Juergen @@ -130,7 +130,7 @@ - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/schemaValidated.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/schemaValidated.xml index 5f22ea3cef42..59b5ef71c3cc 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/schemaValidated.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/schemaValidated.xml @@ -3,16 +3,16 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> - + - + - + - - + + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simpleConstructorNamespaceHandlerTests.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simpleConstructorNamespaceHandlerTests.xml index 0c6b70e59ac5..c8adc6bdb3dc 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simpleConstructorNamespaceHandlerTests.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simpleConstructorNamespaceHandlerTests.xml @@ -5,43 +5,43 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - + - + - + - + - + - + - + \ No newline at end of file diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simpleConstructorNamespaceHandlerTestsWithErrors.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simpleConstructorNamespaceHandlerTestsWithErrors.xml index 932492e9d14c..3dbfb5fae18f 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simpleConstructorNamespaceHandlerTestsWithErrors.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simpleConstructorNamespaceHandlerTestsWithErrors.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simplePropertyNamespaceHandlerTests.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simplePropertyNamespaceHandlerTests.xml index 7b62d39e2792..83e74fad1694 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simplePropertyNamespaceHandlerTests.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simplePropertyNamespaceHandlerTests.xml @@ -4,20 +4,20 @@ xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - + - + - + - + - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simplePropertyNamespaceHandlerTestsWithErrors.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simplePropertyNamespaceHandlerTestsWithErrors.xml index c0335508acc8..b64093d0fc8a 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simplePropertyNamespaceHandlerTestsWithErrors.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/simplePropertyNamespaceHandlerTestsWithErrors.xml @@ -4,10 +4,10 @@ xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - + - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/test.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/test.xml index 3a0b944bef3c..7c4c7596e79f 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/test.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/test.xml @@ -3,7 +3,7 @@ - + I have no properties and I'm happy without them. @@ -12,7 +12,7 @@ - + aliased @@ -20,17 +20,17 @@ - + aliased - + aliased - + @@ -40,7 +40,7 @@ - + Rod 31 @@ -52,29 +52,29 @@ - + Kerry 34 - + Kathy 28 - + typeMismatch 34x - + - true @@ -85,10 +85,10 @@ - + - + false @@ -113,14 +113,14 @@ - + listenerVeto 66 - + - + this is a ]]> diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/testUtilNamespace.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/testUtilNamespace.xml index b2bad9b4c515..972aabcd669a 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/testUtilNamespace.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/testUtilNamespace.xml @@ -17,7 +17,7 @@ name "/> - + @@ -26,13 +26,13 @@ - + - + @@ -50,7 +50,7 @@ + key-type="java.lang.String" value-type="org.springframework.beans.testfixture.beans.TestBean"> @@ -72,7 +72,7 @@ Rob Harrop - + foo @@ -94,13 +94,13 @@ - + - + @@ -116,13 +116,13 @@ min - + - + @@ -152,7 +152,7 @@ - + diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/validateWithDtd.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/validateWithDtd.xml index a872ae9d23d7..fbe7861e21c0 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/validateWithDtd.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/validateWithDtd.xml @@ -9,5 +9,5 @@ This is a top level block comment - + \ No newline at end of file diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/validateWithXsd.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/validateWithXsd.xml index c7e8abe5a55b..1e467348ea18 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/validateWithXsd.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/validateWithXsd.xml @@ -7,5 +7,5 @@ This is a top level block comment the parser now --> - + \ No newline at end of file diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/withMeta.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/withMeta.xml index 5126d253957b..5f7388177722 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/withMeta.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/withMeta.xml @@ -4,15 +4,15 @@ xmlns:spring="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> - + - + - + diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/AgeHolder.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/AgeHolder.java similarity index 93% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/AgeHolder.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/AgeHolder.java index adced8acea9a..722cea949646 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/AgeHolder.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/AgeHolder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; public interface AgeHolder { diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/AnnotatedBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/AnnotatedBean.java similarity index 93% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/AnnotatedBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/AnnotatedBean.java index 6b4e063563bd..985cf64de8af 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/AnnotatedBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/AnnotatedBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; /** * @author Stephane Nicoll diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/BooleanTestBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/BooleanTestBean.java similarity index 95% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/BooleanTestBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/BooleanTestBean.java index bf6105ca55f6..5edbd304eddc 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/BooleanTestBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/BooleanTestBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; /** * @author Juergen Hoeller diff --git a/spring-beans/src/test/java/org/springframework/tests/beans/CollectingReaderEventListener.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/CollectingReaderEventListener.java similarity index 82% rename from spring-beans/src/test/java/org/springframework/tests/beans/CollectingReaderEventListener.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/CollectingReaderEventListener.java index c46e59c3665a..cd213a406db4 100644 --- a/spring-beans/src/test/java/org/springframework/tests/beans/CollectingReaderEventListener.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/CollectingReaderEventListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 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. @@ -14,13 +14,12 @@ * limitations under the License. */ -package org.springframework.tests.beans; +package org.springframework.beans.testfixture.beans; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -36,13 +35,13 @@ */ public class CollectingReaderEventListener implements ReaderEventListener { - private final List defaults = new LinkedList<>(); + private final List defaults = new ArrayList<>(); private final Map componentDefinitions = new LinkedHashMap<>(8); private final Map> aliasMap = new LinkedHashMap<>(8); - private final List imports = new LinkedList<>(); + private final List imports = new ArrayList<>(); @Override @@ -65,16 +64,12 @@ public ComponentDefinition getComponentDefinition(String name) { public ComponentDefinition[] getComponentDefinitions() { Collection collection = this.componentDefinitions.values(); - return collection.toArray(new ComponentDefinition[collection.size()]); + return collection.toArray(new ComponentDefinition[0]); } @Override public void aliasRegistered(AliasDefinition aliasDefinition) { - List aliases = this.aliasMap.get(aliasDefinition.getBeanName()); - if (aliases == null) { - aliases = new ArrayList<>(); - this.aliasMap.put(aliasDefinition.getBeanName(), aliases); - } + List aliases = this.aliasMap.computeIfAbsent(aliasDefinition.getBeanName(), k -> new ArrayList<>()); aliases.add(aliasDefinition); } diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/Colour.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/Colour.java similarity index 95% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/Colour.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/Colour.java index a1910a9dfa41..531149005197 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/Colour.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/Colour.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; /** * @author Rob Harrop diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/CountingTestBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/CountingTestBean.java similarity index 93% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/CountingTestBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/CountingTestBean.java index 74faeba68b85..aa9083a88e11 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/CountingTestBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/CountingTestBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; /** diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/CustomEnum.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/CustomEnum.java similarity index 93% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/CustomEnum.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/CustomEnum.java index dec612088da0..de0d721943d8 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/CustomEnum.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/CustomEnum.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; /** * @author Juergen Hoeller diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/DependenciesBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/DependenciesBean.java similarity index 96% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/DependenciesBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/DependenciesBean.java index 4f0e958c7f74..f990781e11df 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/DependenciesBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/DependenciesBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/DerivedTestBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/DerivedTestBean.java similarity index 97% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/DerivedTestBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/DerivedTestBean.java index 92c62dd5da87..c0e9c2e1dca4 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/DerivedTestBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/DerivedTestBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; import java.io.Serializable; diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/DummyBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/DummyBean.java similarity index 96% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/DummyBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/DummyBean.java index cae1b3993305..cac5cced1c8a 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/DummyBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/DummyBean.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; /** * @author Costin Leau diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/DummyFactory.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/DummyFactory.java similarity index 98% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/DummyFactory.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/DummyFactory.java index 3939fc9372d6..2cf683a7c5fb 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/DummyFactory.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/DummyFactory.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; diff --git a/spring-context/src/test/java/org/springframework/tests/sample/beans/Employee.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/Employee.java similarity index 88% rename from spring-context/src/test/java/org/springframework/tests/sample/beans/Employee.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/Employee.java index 86c3eedb19d8..3e9da7609e35 100644 --- a/spring-context/src/test/java/org/springframework/tests/sample/beans/Employee.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/Employee.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2019 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; public class Employee extends TestBean { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/FactoryMethods.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/FactoryMethods.java similarity index 92% rename from spring-beans/src/test/java/org/springframework/beans/factory/xml/FactoryMethods.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/FactoryMethods.java index 49fb74a1dd4f..98b26798a2c8 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/FactoryMethods.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/FactoryMethods.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2019 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. @@ -14,13 +14,11 @@ * limitations under the License. */ -package org.springframework.beans.factory.xml; +package org.springframework.beans.testfixture.beans; import java.util.Collections; import java.util.List; -import org.springframework.tests.sample.beans.TestBean; - /** * Test class for Spring's ability to create objects using static * factory methods, rather than constructors. @@ -47,7 +45,7 @@ public static FactoryMethods newInstance(TestBean tb) { return new FactoryMethods(tb, "default", 0); } - protected static FactoryMethods newInstance(TestBean tb, int num, String name) { + public static FactoryMethods newInstance(TestBean tb, int num, String name) { if (name == null) { throw new IllegalStateException("Should never be called with null value"); } diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/GenericBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/GenericBean.java similarity index 99% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/GenericBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/GenericBean.java index 26b536b78664..2ad5ece17f5e 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/GenericBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/GenericBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; import java.util.ArrayList; import java.util.Collection; diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/GenericIntegerBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/GenericIntegerBean.java similarity index 93% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/GenericIntegerBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/GenericIntegerBean.java index 398ca1e06cd8..69fd56d17472 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/GenericIntegerBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/GenericIntegerBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; /** diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/GenericSetOfIntegerBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/GenericSetOfIntegerBean.java similarity index 93% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/GenericSetOfIntegerBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/GenericSetOfIntegerBean.java index cbd0a043a6b9..c0439f9fb669 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/GenericSetOfIntegerBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/GenericSetOfIntegerBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; import java.util.Set; diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/HasMap.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/HasMap.java similarity index 97% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/HasMap.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/HasMap.java index 29359e4c5581..c874077bd1b2 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/HasMap.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/HasMap.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; import java.util.IdentityHashMap; import java.util.List; diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/INestedTestBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/INestedTestBean.java similarity index 92% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/INestedTestBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/INestedTestBean.java index adf4f46168fb..3107e6b5f21c 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/INestedTestBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/INestedTestBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; public interface INestedTestBean { diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/IOther.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/IOther.java similarity index 92% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/IOther.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/IOther.java index f674ff7bb712..05059bcf6873 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/IOther.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/IOther.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; public interface IOther { diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/ITestBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/ITestBean.java similarity index 92% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/ITestBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/ITestBean.java index 1fe055dac0b6..742b39c4ea7e 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/ITestBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/ITestBean.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; import java.io.IOException; /** - * Interface used for {@link org.springframework.tests.sample.beans.TestBean}. + * Interface used for {@link org.springframework.beans.testfixture.beans.TestBean}. * *

Two methods are the same as on Person, but if this * extends person it breaks quite a few tests.. diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/IndexedTestBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/IndexedTestBean.java similarity index 98% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/IndexedTestBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/IndexedTestBean.java index 2c8f85189fc7..02948f7eb854 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/IndexedTestBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/IndexedTestBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; import java.util.ArrayList; import java.util.Collection; diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/LifecycleBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/LifecycleBean.java similarity index 98% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/LifecycleBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/LifecycleBean.java index 67712dd76079..b2e54028270d 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/LifecycleBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/LifecycleBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/MustBeInitialized.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/MustBeInitialized.java similarity index 96% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/MustBeInitialized.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/MustBeInitialized.java index 2dde17d219f4..2e53ce2f885a 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/MustBeInitialized.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/MustBeInitialized.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; import org.springframework.beans.factory.InitializingBean; diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/NestedTestBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/NestedTestBean.java similarity index 96% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/NestedTestBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/NestedTestBean.java index 46e6a9110f67..ea26ec0072c0 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/NestedTestBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/NestedTestBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; /** * Simple nested test bean used for testing bean factories, AOP framework etc. diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/NumberTestBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/NumberTestBean.java similarity index 97% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/NumberTestBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/NumberTestBean.java index 08b1649d42ad..224965b5c7a6 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/NumberTestBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/NumberTestBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; import java.math.BigDecimal; import java.math.BigInteger; diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/PackageLevelVisibleBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/PackageLevelVisibleBean.java similarity index 94% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/PackageLevelVisibleBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/PackageLevelVisibleBean.java index 1f446321132f..73a0bd3b9358 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/PackageLevelVisibleBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/PackageLevelVisibleBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; /** * @see org.springframework.beans.factory.config.FieldRetrievingFactoryBeanTests diff --git a/spring-aop/src/test/java/org/springframework/tests/sample/beans/Person.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/Person.java similarity index 94% rename from spring-aop/src/test/java/org/springframework/tests/sample/beans/Person.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/Person.java index 1e9acb09646e..d57a8ca4267f 100644 --- a/spring-aop/src/test/java/org/springframework/tests/sample/beans/Person.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/Person.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; /** * diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/Pet.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/Pet.java similarity index 95% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/Pet.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/Pet.java index 5e9040db0c0b..661eff92feb7 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/Pet.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/Pet.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; /** * @author Rob Harrop diff --git a/spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/SerializablePerson.java similarity index 96% rename from spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/SerializablePerson.java index 0496d09a9682..6f9436906cc1 100644 --- a/spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/SerializablePerson.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; import java.io.Serializable; diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/SideEffectBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/SideEffectBean.java similarity index 94% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/SideEffectBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/SideEffectBean.java index ac09e8d76425..36911bdedcf1 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/SideEffectBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/SideEffectBean.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; /** * Bean that changes state on a business invocation, so that diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestAnnotation.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/TestAnnotation.java similarity index 93% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/TestAnnotation.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/TestAnnotation.java index ed1b8d528975..379c87419b5f 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestAnnotation.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/TestAnnotation.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/TestBean.java similarity index 94% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/TestBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/TestBean.java index 1e83a21a13ce..ed54d0d05f4b 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/TestBean.java @@ -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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; import java.io.IOException; import java.util.ArrayList; @@ -22,7 +22,6 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; @@ -75,9 +74,9 @@ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOt private Date date = new Date(); - private Float myFloat = new Float(0.0); + private Float myFloat = Float.valueOf(0.0f); - private Collection friends = new LinkedList<>(); + private Collection friends = new ArrayList<>(); private Set someSet = new HashSet<>(); @@ -421,7 +420,7 @@ public void setPets(List pets) { /** - * @see org.springframework.tests.sample.beans.ITestBean#exceptional(Throwable) + * @see org.springframework.beans.testfixture.beans.ITestBean#exceptional(Throwable) */ @Override public void exceptional(Throwable t) throws Throwable { @@ -435,7 +434,7 @@ public void unreliableFileOperation() throws IOException { throw new IOException(); } /** - * @see org.springframework.tests.sample.beans.ITestBean#returnsThis() + * @see org.springframework.beans.testfixture.beans.ITestBean#returnsThis() */ @Override public Object returnsThis() { @@ -443,7 +442,7 @@ public Object returnsThis() { } /** - * @see org.springframework.tests.sample.beans.IOther#absquatulate() + * @see org.springframework.beans.testfixture.beans.IOther#absquatulate() */ @Override public void absquatulate() { @@ -469,7 +468,7 @@ public boolean equals(Object other) { if (this == other) { return true; } - if (other == null || !(other instanceof TestBean)) { + if (!(other instanceof TestBean)) { return false; } TestBean tb2 = (TestBean) other; diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/factory/DummyFactory.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/factory/DummyFactory.java similarity index 97% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/factory/DummyFactory.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/factory/DummyFactory.java index e707f05853d7..9e3d8b6511a2 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/factory/DummyFactory.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/factory/DummyFactory.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans.factory; +package org.springframework.beans.testfixture.beans.factory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; @@ -24,7 +24,7 @@ import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; /** * Simple factory to allow testing of FactoryBean support in AbstractBeanFactory. diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/package-info.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/package-info.java similarity index 56% rename from spring-beans/src/test/java/org/springframework/tests/sample/beans/package-info.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/package-info.java index 575bd1933f9b..14a1870d42b7 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/package-info.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/package-info.java @@ -1,4 +1,4 @@ /** * General purpose sample beans that can be used with tests. */ -package org.springframework.tests.sample.beans; +package org.springframework.beans.testfixture.beans; diff --git a/spring-aop/src/test/java/org/springframework/tests/sample/beans/subpkg/DeepBean.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/subpkg/DeepBean.java similarity index 74% rename from spring-aop/src/test/java/org/springframework/tests/sample/beans/subpkg/DeepBean.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/subpkg/DeepBean.java index 41dd7e33b9cb..50fc891ad832 100644 --- a/spring-aop/src/test/java/org/springframework/tests/sample/beans/subpkg/DeepBean.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/beans/subpkg/DeepBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2019 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. @@ -14,14 +14,12 @@ * limitations under the License. */ -package org.springframework.tests.sample.beans.subpkg; - -import org.springframework.aop.aspectj.AspectJExpressionPointcutTests; +package org.springframework.beans.testfixture.beans.subpkg; /** * Used for testing pointcut matching. * - * @see AspectJExpressionPointcutTests#testWithinRootAndSubpackages() + * @see org.springframework.aop.aspectj.AspectJExpressionPointcutTests#testWithinRootAndSubpackages() * * @author Chris Beams */ diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/AbstractBeanFactoryTests.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/factory/xml/AbstractBeanFactoryTests.java similarity index 97% rename from spring-beans/src/test/java/org/springframework/beans/factory/xml/AbstractBeanFactoryTests.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/factory/xml/AbstractBeanFactoryTests.java index 31c1984c35c3..307a9d64c12d 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/AbstractBeanFactoryTests.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/factory/xml/AbstractBeanFactoryTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.beans.factory.xml; +package org.springframework.beans.testfixture.factory.xml; import java.beans.PropertyEditorSupport; import java.util.StringTokenizer; @@ -29,10 +29,10 @@ import org.springframework.beans.factory.BeanNotOfRequiredTypeException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.tests.sample.beans.LifecycleBean; -import org.springframework.tests.sample.beans.MustBeInitialized; -import org.springframework.tests.sample.beans.TestBean; -import org.springframework.tests.sample.beans.factory.DummyFactory; +import org.springframework.beans.testfixture.beans.LifecycleBean; +import org.springframework.beans.testfixture.beans.MustBeInitialized; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.beans.testfixture.beans.factory.DummyFactory; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/AbstractListableBeanFactoryTests.java b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/factory/xml/AbstractListableBeanFactoryTests.java similarity index 91% rename from spring-beans/src/test/java/org/springframework/beans/factory/xml/AbstractListableBeanFactoryTests.java rename to spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/factory/xml/AbstractListableBeanFactoryTests.java index dbae0ebed703..0717f8a49366 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/AbstractListableBeanFactoryTests.java +++ b/spring-beans/src/testFixtures/java/org/springframework/beans/testfixture/factory/xml/AbstractListableBeanFactoryTests.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package org.springframework.beans.factory.xml; +package org.springframework.beans.testfixture.factory.xml; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.ListableBeanFactory; -import org.springframework.tests.sample.beans.TestBean; +import org.springframework.beans.testfixture.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; @@ -55,13 +55,13 @@ protected final void assertCount(int count) { protected void assertTestBeanCount(int count) { String[] defNames = getListableBeanFactory().getBeanNamesForType(TestBean.class, true, false); - assertThat(defNames.length == count).as("We should have " + count + " beans for class org.springframework.tests.sample.beans.TestBean, not " + + assertThat(defNames.length == count).as("We should have " + count + " beans for class org.springframework.beans.testfixture.beans.TestBean, not " + defNames.length).isTrue(); int countIncludingFactoryBeans = count + 2; String[] names = getListableBeanFactory().getBeanNamesForType(TestBean.class, true, true); assertThat(names.length == countIncludingFactoryBeans).as("We should have " + countIncludingFactoryBeans + - " beans for class org.springframework.tests.sample.beans.TestBean, not " + names.length).isTrue(); + " beans for class org.springframework.beans.testfixture.beans.TestBean, not " + names.length).isTrue(); } @Test diff --git a/spring-context-indexer/src/main/java/org/springframework/context/index/processor/CandidateComponentsIndexer.java b/spring-context-indexer/src/main/java/org/springframework/context/index/processor/CandidateComponentsIndexer.java index 6fd4bda2bf44..fcd0d63e12f4 100644 --- a/spring-context-indexer/src/main/java/org/springframework/context/index/processor/CandidateComponentsIndexer.java +++ b/spring-context-indexer/src/main/java/org/springframework/context/index/processor/CandidateComponentsIndexer.java @@ -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. @@ -137,7 +137,7 @@ private static List staticTypesIn(Iterable eleme List list = new ArrayList<>(); for (Element element : elements) { if (TYPE_KINDS.contains(element.getKind()) && element.getModifiers().contains(Modifier.STATIC)) { - list.add(TypeElement.class.cast(element)); + list.add((TypeElement) element); } } return list; diff --git a/spring-context-indexer/src/main/java/org/springframework/context/index/processor/SortedProperties.java b/spring-context-indexer/src/main/java/org/springframework/context/index/processor/SortedProperties.java index 0281cf4b68da..127a1a2b9614 100644 --- a/spring-context-indexer/src/main/java/org/springframework/context/index/processor/SortedProperties.java +++ b/spring-context-indexer/src/main/java/org/springframework/context/index/processor/SortedProperties.java @@ -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. @@ -88,7 +88,7 @@ class SortedProperties extends Properties { public void store(OutputStream out, String comments) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); super.store(baos, (this.omitComments ? null : comments)); - String contents = new String(baos.toByteArray(), StandardCharsets.ISO_8859_1); + String contents = baos.toString(StandardCharsets.ISO_8859_1.name()); for (String line : contents.split(EOL)) { if (!(this.omitComments && line.startsWith("#"))) { out.write((line + EOL).getBytes(StandardCharsets.ISO_8859_1)); diff --git a/spring-context-indexer/src/main/java/org/springframework/context/index/processor/TypeHelper.java b/spring-context-indexer/src/main/java/org/springframework/context/index/processor/TypeHelper.java index 7daaeece334a..470c0398a235 100644 --- a/spring-context-indexer/src/main/java/org/springframework/context/index/processor/TypeHelper.java +++ b/spring-context-indexer/src/main/java/org/springframework/context/index/processor/TypeHelper.java @@ -63,7 +63,7 @@ public String getType(TypeMirror type) { if (type instanceof DeclaredType) { DeclaredType declaredType = (DeclaredType) type; Element enclosingElement = declaredType.asElement().getEnclosingElement(); - if (enclosingElement != null && enclosingElement instanceof TypeElement) { + if (enclosingElement instanceof TypeElement) { return getQualifiedName(enclosingElement) + "$" + declaredType.asElement().getSimpleName().toString(); } else { diff --git a/spring-context-indexer/src/test/java/org/springframework/context/index/sample/SampleNone.java b/spring-context-indexer/src/test/java/org/springframework/context/index/sample/SampleNone.java index bd1cd72401f2..f81de9365b85 100644 --- a/spring-context-indexer/src/test/java/org/springframework/context/index/sample/SampleNone.java +++ b/spring-context-indexer/src/test/java/org/springframework/context/index/sample/SampleNone.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2019 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,7 +17,6 @@ package org.springframework.context.index.sample; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.core.type.Scope; /** * Candidate with no matching annotation. diff --git a/spring-context/src/test/java/org/springframework/context/TestListener.java b/spring-context-indexer/src/test/java/org/springframework/context/index/sample/Scope.java similarity index 54% rename from spring-context/src/test/java/org/springframework/context/TestListener.java rename to spring-context-indexer/src/test/java/org/springframework/context/index/sample/Scope.java index bc8949f2700d..b96de6139374 100644 --- a/spring-context/src/test/java/org/springframework/context/TestListener.java +++ b/spring-context-indexer/src/test/java/org/springframework/context/index/sample/Scope.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2009 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. @@ -14,29 +14,20 @@ * limitations under the License. */ -package org.springframework.context; +package org.springframework.context.index.sample; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** - * Listener that maintains a global count of events. - * - * @author Rod Johnson - * @since January 21, 2001 + * Copy of the {@code @Scope} annotation for testing purposes. */ -public class TestListener implements ApplicationListener { - - private int eventCount; - - public int getEventCount() { - return eventCount; - } - - public void zeroCounter() { - eventCount = 0; - } +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface Scope { - @Override - public void onApplicationEvent(ApplicationEvent e) { - ++eventCount; - } + String value() default "singleton"; } diff --git a/spring-context-indexer/src/test/java/org/springframework/context/index/test/TestCompiler.java b/spring-context-indexer/src/test/java/org/springframework/context/index/test/TestCompiler.java index 2650c8a9f9ea..ee0eca1386fe 100644 --- a/spring-context-indexer/src/test/java/org/springframework/context/index/test/TestCompiler.java +++ b/spring-context-indexer/src/test/java/org/springframework/context/index/test/TestCompiler.java @@ -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,8 +21,6 @@ import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; import javax.annotation.processing.Processor; import javax.tools.JavaCompiler; @@ -63,8 +61,7 @@ public TestCompiler(JavaCompiler compiler, Path tempDir) throws IOException { public TestCompilationTask getTask(Class... types) { - List names = Arrays.stream(types).map(Class::getName).collect(Collectors.toList()); - return getTask(names.toArray(new String[names.size()])); + return getTask(Arrays.stream(types).map(Class::getName).toArray(String[]::new)); } public TestCompilationTask getTask(String... types) { diff --git a/spring-context-support/spring-context-support.gradle b/spring-context-support/spring-context-support.gradle index 54bf50e43335..f8a8631293ad 100644 --- a/spring-context-support/spring-context-support.gradle +++ b/spring-context-support/spring-context-support.gradle @@ -15,6 +15,10 @@ dependencies { optional("org.codehaus.fabric3.api:commonj") optional("org.freemarker:freemarker") testCompile(project(":spring-context")) + testCompile(testFixtures(project(":spring-beans"))) + testCompile(testFixtures(project(":spring-context"))) + testCompile(testFixtures(project(":spring-core"))) + testCompile(testFixtures(project(":spring-tx"))) testCompile("org.hsqldb:hsqldb") testCompile("org.hibernate:hibernate-validator") testCompile("javax.annotation:javax.annotation-api") @@ -22,4 +26,7 @@ dependencies { testRuntime("org.ehcache:ehcache") testRuntime("org.glassfish:javax.el") testRuntime("com.sun.mail:javax.mail") + testFixturesApi("org.junit.jupiter:junit-jupiter-api") + testFixturesImplementation("org.assertj:assertj-core") + testFixturesImplementation("org.mockito:mockito-core") } diff --git a/spring-context-support/src/main/java/org/springframework/cache/caffeine/CaffeineCache.java b/spring-context-support/src/main/java/org/springframework/cache/caffeine/CaffeineCache.java index 34dc622f8b2b..ef8c3b03e5bd 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/caffeine/CaffeineCache.java +++ b/spring-context-support/src/main/java/org/springframework/cache/caffeine/CaffeineCache.java @@ -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. @@ -35,6 +35,7 @@ * @author Juergen Hoeller * @author Stephane Nicoll * @since 4.3 + * @see CaffeineCacheManager */ public class CaffeineCache extends AbstractValueAdaptingCache { @@ -82,16 +83,6 @@ public final com.github.benmanes.caffeine.cache.Cache getNativeC return this.cache; } - @Override - @Nullable - public ValueWrapper get(Object key) { - if (this.cache instanceof LoadingCache) { - Object value = ((LoadingCache) this.cache).get(key); - return toValueWrapper(value); - } - return super.get(key); - } - @SuppressWarnings("unchecked") @Override @Nullable @@ -102,6 +93,9 @@ public T get(Object key, final Callable valueLoader) { @Override @Nullable protected Object lookup(Object key) { + if (this.cache instanceof LoadingCache) { + return ((LoadingCache) this.cache).get(key); + } return this.cache.getIfPresent(key); } diff --git a/spring-context-support/src/main/java/org/springframework/cache/caffeine/CaffeineCacheManager.java b/spring-context-support/src/main/java/org/springframework/cache/caffeine/CaffeineCacheManager.java index 54331f8909d0..d5b77cbc1bb9 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/caffeine/CaffeineCacheManager.java +++ b/spring-context-support/src/main/java/org/springframework/cache/caffeine/CaffeineCacheManager.java @@ -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. @@ -21,7 +21,7 @@ import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.Caffeine; @@ -50,15 +50,12 @@ * @author Ben Manes * @author Juergen Hoeller * @author Stephane Nicoll + * @author Sam Brannen * @since 4.3 * @see CaffeineCache */ public class CaffeineCacheManager implements CacheManager { - private final ConcurrentMap cacheMap = new ConcurrentHashMap<>(16); - - private boolean dynamic = true; - private Caffeine cacheBuilder = Caffeine.newBuilder(); @Nullable @@ -66,6 +63,12 @@ public class CaffeineCacheManager implements CacheManager { private boolean allowNullValues = true; + private boolean dynamic = true; + + private final Map cacheMap = new ConcurrentHashMap<>(16); + + private final Collection customCacheNames = new CopyOnWriteArrayList<>(); + /** * Construct a dynamic CaffeineCacheManager, @@ -134,6 +137,13 @@ public void setCacheSpecification(String cacheSpecification) { doSetCaffeine(Caffeine.from(cacheSpecification)); } + private void doSetCaffeine(Caffeine cacheBuilder) { + if (!ObjectUtils.nullSafeEquals(this.cacheBuilder, cacheBuilder)) { + this.cacheBuilder = cacheBuilder; + refreshCommonCaches(); + } + } + /** * Set the Caffeine CacheLoader to use for building each individual * {@link CaffeineCache} instance, turning it into a LoadingCache. @@ -144,7 +154,7 @@ public void setCacheSpecification(String cacheSpecification) { public void setCacheLoader(CacheLoader cacheLoader) { if (!ObjectUtils.nullSafeEquals(this.cacheLoader, cacheLoader)) { this.cacheLoader = cacheLoader; - refreshKnownCaches(); + refreshCommonCaches(); } } @@ -157,7 +167,7 @@ public void setCacheLoader(CacheLoader cacheLoader) { public void setAllowNullValues(boolean allowNullValues) { if (this.allowNullValues != allowNullValues) { this.allowNullValues = allowNullValues; - refreshKnownCaches(); + refreshCommonCaches(); } } @@ -178,55 +188,80 @@ public Collection getCacheNames() { @Override @Nullable public Cache getCache(String name) { - Cache cache = this.cacheMap.get(name); - if (cache == null && this.dynamic) { - synchronized (this.cacheMap) { - cache = this.cacheMap.get(name); - if (cache == null) { - cache = createCaffeineCache(name); - this.cacheMap.put(name, cache); - } - } - } - return cache; + return this.cacheMap.computeIfAbsent(name, cacheName -> + this.dynamic ? createCaffeineCache(cacheName) : null); } + /** - * Create a new CaffeineCache instance for the specified cache name. + * Register the given native Caffeine Cache instance with this cache manager, + * adapting it to Spring's cache API for exposure through {@link #getCache}. + * Any number of such custom caches may be registered side by side. + *

This allows for custom settings per cache (as opposed to all caches + * sharing the common settings in the cache manager's configuration) and + * is typically used with the Caffeine builder API: + * {@code registerCustomCache("myCache", Caffeine.newBuilder().maximumSize(10).build())} + *

Note that any other caches, whether statically specified through + * {@link #setCacheNames} or dynamically built on demand, still operate + * with the common settings in the cache manager's configuration. + * @param name the name of the cache + * @param cache the custom Caffeine Cache instance to register + * @since 5.2.8 + * @see #adaptCaffeineCache + */ + public void registerCustomCache(String name, com.github.benmanes.caffeine.cache.Cache cache) { + this.customCacheNames.add(name); + this.cacheMap.put(name, adaptCaffeineCache(name, cache)); + } + + /** + * Adapt the given new native Caffeine Cache instance to Spring's {@link Cache} + * abstraction for the specified cache name. * @param name the name of the cache + * @param cache the native Caffeine Cache instance * @return the Spring CaffeineCache adapter (or a decorator thereof) + * @since 5.2.8 + * @see CaffeineCache + * @see #isAllowNullValues() + */ + protected Cache adaptCaffeineCache(String name, com.github.benmanes.caffeine.cache.Cache cache) { + return new CaffeineCache(name, cache, isAllowNullValues()); + } + + /** + * Build a common {@link CaffeineCache} instance for the specified cache name, + * using the common Caffeine configuration specified on this cache manager. + *

Delegates to {@link #adaptCaffeineCache} as the adaptation method to + * Spring's cache abstraction (allowing for centralized decoration etc), + * passing in a freshly built native Caffeine Cache instance. + * @param name the name of the cache + * @return the Spring CaffeineCache adapter (or a decorator thereof) + * @see #adaptCaffeineCache + * @see #createNativeCaffeineCache */ protected Cache createCaffeineCache(String name) { - return new CaffeineCache(name, createNativeCaffeineCache(name), isAllowNullValues()); + return adaptCaffeineCache(name, createNativeCaffeineCache(name)); } /** - * Create a native Caffeine Cache instance for the specified cache name. + * Build a common Caffeine Cache instance for the specified cache name, + * using the common Caffeine configuration specified on this cache manager. * @param name the name of the cache * @return the native Caffeine Cache instance + * @see #createCaffeineCache */ protected com.github.benmanes.caffeine.cache.Cache createNativeCaffeineCache(String name) { - if (this.cacheLoader != null) { - return this.cacheBuilder.build(this.cacheLoader); - } - else { - return this.cacheBuilder.build(); - } - } - - private void doSetCaffeine(Caffeine cacheBuilder) { - if (!ObjectUtils.nullSafeEquals(this.cacheBuilder, cacheBuilder)) { - this.cacheBuilder = cacheBuilder; - refreshKnownCaches(); - } + return (this.cacheLoader != null ? this.cacheBuilder.build(this.cacheLoader) : this.cacheBuilder.build()); } /** - * Create the known caches again with the current state of this manager. + * Recreate the common caches with the current state of this manager. */ - private void refreshKnownCaches() { + private void refreshCommonCaches() { for (Map.Entry entry : this.cacheMap.entrySet()) { - entry.setValue(createCaffeineCache(entry.getKey())); + if (!this.customCacheNames.contains(entry.getKey())) { + entry.setValue(createCaffeineCache(entry.getKey())); + } } } diff --git a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCache.java b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCache.java index 26ea8929d3a6..4309fa73a2cb 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCache.java +++ b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCache.java @@ -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. @@ -34,6 +34,7 @@ * @author Juergen Hoeller * @author Stephane Nicoll * @since 3.1 + * @see EhCacheCacheManager */ public class EhCacheCache implements Cache { diff --git a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCacheManager.java b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCacheManager.java index 709404e6f2dc..f3e58a55b288 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCacheManager.java +++ b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCacheManager.java @@ -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. @@ -34,6 +34,7 @@ * @author Juergen Hoeller * @author Stephane Nicoll * @since 3.1 + * @see EhCacheCache */ public class EhCacheCacheManager extends AbstractTransactionSupportingCacheManager { diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCache.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCache.java index 73c6176a5e99..84d2e3f9bcf2 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCache.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCache.java @@ -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 @@ * @author Juergen Hoeller * @author Stephane Nicoll * @since 3.2 + * @see JCacheCacheManager */ public class JCacheCache extends AbstractValueAdaptingCache { diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCacheManager.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCacheManager.java index 08c2b0ec27c1..e4feb09554ba 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCacheManager.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCacheManager.java @@ -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. @@ -36,6 +36,7 @@ * @author Juergen Hoeller * @author Stephane Nicoll * @since 3.2 + * @see JCacheCache */ public class JCacheCacheManager extends AbstractTransactionSupportingCacheManager { diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/config/AbstractJCacheConfiguration.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/config/AbstractJCacheConfiguration.java index 8f751a990082..1cd5ce612f30 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/config/AbstractJCacheConfiguration.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/config/AbstractJCacheConfiguration.java @@ -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,8 +38,8 @@ * @since 4.1 * @see JCacheConfigurer */ -@Configuration -public class AbstractJCacheConfiguration extends AbstractCachingConfiguration { +@Configuration(proxyBeanMethods = false) +public abstract class AbstractJCacheConfiguration extends AbstractCachingConfiguration { @Nullable protected Supplier exceptionCacheResolver; diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/config/ProxyJCacheConfiguration.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/config/ProxyJCacheConfiguration.java index c779bfc10ae4..c999000c52fd 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/config/ProxyJCacheConfiguration.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/config/ProxyJCacheConfiguration.java @@ -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. @@ -20,6 +20,7 @@ import org.springframework.cache.config.CacheManagementConfigUtils; import org.springframework.cache.jcache.interceptor.BeanFactoryJCacheOperationSourceAdvisor; import org.springframework.cache.jcache.interceptor.JCacheInterceptor; +import org.springframework.cache.jcache.interceptor.JCacheOperationSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Role; @@ -36,17 +37,18 @@ * @see org.springframework.cache.annotation.EnableCaching * @see org.springframework.cache.annotation.CachingConfigurationSelector */ -@Configuration +@Configuration(proxyBeanMethods = false) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class ProxyJCacheConfiguration extends AbstractJCacheConfiguration { @Bean(name = CacheManagementConfigUtils.JCACHE_ADVISOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - public BeanFactoryJCacheOperationSourceAdvisor cacheAdvisor() { - BeanFactoryJCacheOperationSourceAdvisor advisor = - new BeanFactoryJCacheOperationSourceAdvisor(); - advisor.setCacheOperationSource(cacheOperationSource()); - advisor.setAdvice(cacheInterceptor()); + public BeanFactoryJCacheOperationSourceAdvisor cacheAdvisor( + JCacheOperationSource jCacheOperationSource, JCacheInterceptor jCacheInterceptor) { + + BeanFactoryJCacheOperationSourceAdvisor advisor = new BeanFactoryJCacheOperationSourceAdvisor(); + advisor.setCacheOperationSource(jCacheOperationSource); + advisor.setAdvice(jCacheInterceptor); if (this.enableCaching != null) { advisor.setOrder(this.enableCaching.getNumber("order")); } @@ -55,9 +57,9 @@ public BeanFactoryJCacheOperationSourceAdvisor cacheAdvisor() { @Bean(name = "jCacheInterceptor") @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - public JCacheInterceptor cacheInterceptor() { + public JCacheInterceptor cacheInterceptor(JCacheOperationSource jCacheOperationSource) { JCacheInterceptor interceptor = new JCacheInterceptor(this.errorHandler); - interceptor.setCacheOperationSource(cacheOperationSource()); + interceptor.setCacheOperationSource(jCacheOperationSource); return interceptor; } diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheAspectSupport.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheAspectSupport.java index 893e4a6b792b..1dbf3c8ae6d4 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheAspectSupport.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheAspectSupport.java @@ -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. @@ -165,6 +165,7 @@ else if (operation instanceof CacheRemoveAllOperation) { * @return the result of the invocation * @see CacheOperationInvoker#invoke() */ + @Nullable protected Object invokeOperation(CacheOperationInvoker invoker) { return invoker.invoke(); } diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheInterceptor.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheInterceptor.java index c4835a2c4206..81e65d1a1c1c 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheInterceptor.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/JCacheInterceptor.java @@ -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. @@ -27,6 +27,7 @@ import org.springframework.cache.interceptor.CacheOperationInvoker; import org.springframework.cache.interceptor.SimpleCacheErrorHandler; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.springframework.util.function.SingletonSupplier; /** @@ -78,8 +79,10 @@ public Object invoke(final MethodInvocation invocation) throws Throwable { } }; + Object target = invocation.getThis(); + Assert.state(target != null, "Target must not be null"); try { - return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments()); + return execute(aopAllianceInvoker, target, method, invocation.getArguments()); } catch (CacheOperationInvoker.ThrowableWrapper th) { throw th.getOriginal(); diff --git a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/KeyGeneratorAdapter.java b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/KeyGeneratorAdapter.java index 78a8c4e00a27..3fcfe9fc58d9 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/KeyGeneratorAdapter.java +++ b/spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/KeyGeneratorAdapter.java @@ -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. @@ -87,7 +87,7 @@ public Object getTarget() { @Override public Object generate(Object target, Method method, Object... params) { JCacheOperation operation = this.cacheOperationSource.getCacheOperation(method, target.getClass()); - if (!(AbstractJCacheKeyOperation.class.isInstance(operation))) { + if (!(operation instanceof AbstractJCacheKeyOperation)) { throw new IllegalStateException("Invalid operation, should be a key-based operation " + operation); } CacheKeyInvocationContext invocationContext = createCacheKeyInvocationContext(target, operation, params); @@ -101,7 +101,6 @@ public Object generate(Object target, Method method, Object... params) { } } - @SuppressWarnings("unchecked") private static Object doGenerate(KeyGenerator keyGenerator, CacheKeyInvocationContext context) { List parameters = new ArrayList<>(); for (CacheInvocationParameter param : context.getKeyParameters()) { diff --git a/spring-context-support/src/main/java/org/springframework/cache/transaction/TransactionAwareCacheDecorator.java b/spring-context-support/src/main/java/org/springframework/cache/transaction/TransactionAwareCacheDecorator.java index ca193ee0d8f3..22fcaffd5f1c 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/transaction/TransactionAwareCacheDecorator.java +++ b/spring-context-support/src/main/java/org/springframework/cache/transaction/TransactionAwareCacheDecorator.java @@ -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. @@ -20,7 +20,7 @@ import org.springframework.cache.Cache; import org.springframework.lang.Nullable; -import org.springframework.transaction.support.TransactionSynchronizationAdapter; +import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; @@ -94,7 +94,7 @@ public T get(Object key, Callable valueLoader) { @Override public void put(final Object key, @Nullable final Object value) { if (TransactionSynchronizationManager.isSynchronizationActive()) { - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void afterCommit() { TransactionAwareCacheDecorator.this.targetCache.put(key, value); @@ -115,7 +115,7 @@ public ValueWrapper putIfAbsent(Object key, @Nullable Object value) { @Override public void evict(final Object key) { if (TransactionSynchronizationManager.isSynchronizationActive()) { - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void afterCommit() { TransactionAwareCacheDecorator.this.targetCache.evict(key); @@ -135,7 +135,7 @@ public boolean evictIfPresent(Object key) { @Override public void clear() { if (TransactionSynchronizationManager.isSynchronizationActive()) { - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void afterCommit() { targetCache.clear(); diff --git a/spring-context-support/src/main/java/org/springframework/mail/SimpleMailMessage.java b/spring-context-support/src/main/java/org/springframework/mail/SimpleMailMessage.java index 303836769542..95268a2993e0 100644 --- a/spring-context-support/src/main/java/org/springframework/mail/SimpleMailMessage.java +++ b/spring-context-support/src/main/java/org/springframework/mail/SimpleMailMessage.java @@ -276,9 +276,7 @@ private static String[] copyOrNull(@Nullable String[] state) { } private static String[] copy(String[] state) { - String[] copy = new String[state.length]; - System.arraycopy(state, 0, copy, 0, state.length); - return copy; + return state.clone(); } } diff --git a/spring-context-support/src/main/java/org/springframework/mail/javamail/ConfigurableMimeFileTypeMap.java b/spring-context-support/src/main/java/org/springframework/mail/javamail/ConfigurableMimeFileTypeMap.java index cc6933129d8e..ff6921b37183 100644 --- a/spring-context-support/src/main/java/org/springframework/mail/javamail/ConfigurableMimeFileTypeMap.java +++ b/spring-context-support/src/main/java/org/springframework/mail/javamail/ConfigurableMimeFileTypeMap.java @@ -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. @@ -146,13 +146,9 @@ protected final FileTypeMap getFileTypeMap() { protected FileTypeMap createFileTypeMap(@Nullable Resource mappingLocation, @Nullable String[] mappings) throws IOException { MimetypesFileTypeMap fileTypeMap = null; if (mappingLocation != null) { - InputStream is = mappingLocation.getInputStream(); - try { + try (InputStream is = mappingLocation.getInputStream()) { fileTypeMap = new MimetypesFileTypeMap(is); } - finally { - is.close(); - } } else { fileTypeMap = new MimetypesFileTypeMap(); diff --git a/spring-context-support/src/main/java/org/springframework/mail/javamail/MimeMessageHelper.java b/spring-context-support/src/main/java/org/springframework/mail/javamail/MimeMessageHelper.java index b5e37e3698ab..f795390fa24d 100644 --- a/spring-context-support/src/main/java/org/springframework/mail/javamail/MimeMessageHelper.java +++ b/spring-context-support/src/main/java/org/springframework/mail/javamail/MimeMessageHelper.java @@ -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. @@ -159,8 +159,6 @@ public class MimeMessageHelper { private static final String HEADER_PRIORITY = "X-Priority"; - private static final String HEADER_CONTENT_ID = "Content-ID"; - private final MimeMessage mimeMessage; @@ -175,6 +173,8 @@ public class MimeMessageHelper { private FileTypeMap fileTypeMap; + private boolean encodeFilenames = false; + private boolean validateAddresses = false; @@ -464,7 +464,7 @@ protected FileTypeMap getDefaultFileTypeMap(MimeMessage mimeMessage) { * Set the Java Activation Framework {@code FileTypeMap} to use * for determining the content type of inline content and attachments * that get added to the message. - *

Default is the {@code FileTypeMap} that the underlying + *

The default is the {@code FileTypeMap} that the underlying * MimeMessage carries, if any, or the Activation Framework's default * {@code FileTypeMap} instance else. * @see #addInline @@ -480,18 +480,42 @@ public void setFileTypeMap(@Nullable FileTypeMap fileTypeMap) { /** * Return the {@code FileTypeMap} used by this MimeMessageHelper. + * @see #setFileTypeMap */ public FileTypeMap getFileTypeMap() { return this.fileTypeMap; } + /** + * Set whether to encode attachment filenames passed to this helper's + * {@code #addAttachment} methods. + *

The default is {@code false} for standard MIME behavior; turn this to + * {@code true} for compatibility with older email clients. On a related note, + * check out JavaMail's {@code mail.mime.encodefilename} system property. + *

NOTE: The default changed to {@code false} in 5.3, in favor of + * JavaMail's standard {@code mail.mime.encodefilename} system property. + * @since 5.2.9 + * @see #addAttachment(String, DataSource) + * @see MimeBodyPart#setFileName(String) + */ + public void setEncodeFilenames(boolean encodeFilenames) { + this.encodeFilenames = encodeFilenames; + } + + /** + * Return whether to encode attachment filenames passed to this helper's + * {@code #addAttachment} methods. + * @since 5.2.9 + * @see #setEncodeFilenames + */ + public boolean isEncodeFilenames() { + return this.encodeFilenames; + } + /** * Set whether to validate all addresses which get passed to this helper. - * Default is "false". - *

Note that this is by default just available for JavaMail >= 1.3. - * You can override the default {@code validateAddress method} for - * validation on older JavaMail versions (or for custom validation). + *

The default is {@code false}. * @see #validateAddress */ public void setValidateAddresses(boolean validateAddresses) { @@ -500,6 +524,7 @@ public void setValidateAddresses(boolean validateAddresses) { /** * Return whether this helper will validate all addresses passed to it. + * @see #setValidateAddresses */ public boolean isValidateAddresses() { return this.validateAddresses; @@ -508,10 +533,8 @@ public boolean isValidateAddresses() { /** * Validate the given mail address. * Called by all of MimeMessageHelper's address setters and adders. - *

Default implementation invokes {@code InternetAddress.validate()}, + *

The default implementation invokes {@link InternetAddress#validate()}, * provided that address validation is activated for the helper instance. - *

Note that this method will just work on JavaMail >= 1.3. You can override - * it for validation on older JavaMail versions or for custom validation. * @param address the address to validate * @throws AddressException if validation failed * @see #isValidateAddresses() @@ -525,7 +548,8 @@ protected void validateAddress(InternetAddress address) throws AddressException /** * Validate all given mail addresses. - * Default implementation simply delegates to validateAddress for each address. + *

The default implementation simply delegates to {@link #validateAddress} + * for each address. * @param addresses the addresses to validate * @throws AddressException if validation failed * @see #validateAddress(InternetAddress) @@ -885,9 +909,7 @@ public void addInline(String contentId, DataSource dataSource) throws MessagingE Assert.notNull(dataSource, "DataSource must not be null"); MimeBodyPart mimeBodyPart = new MimeBodyPart(); mimeBodyPart.setDisposition(MimeBodyPart.INLINE); - // We're using setHeader here to remain compatible with JavaMail 1.2, - // rather than JavaMail 1.3's setContentID. - mimeBodyPart.setHeader(HEADER_CONTENT_ID, "<" + contentId + ">"); + mimeBodyPart.setContentID("<" + contentId + ">"); mimeBodyPart.setDataHandler(new DataHandler(dataSource)); getMimeMultipart().addBodyPart(mimeBodyPart); } @@ -997,7 +1019,8 @@ public void addAttachment(String attachmentFilename, DataSource dataSource) thro try { MimeBodyPart mimeBodyPart = new MimeBodyPart(); mimeBodyPart.setDisposition(MimeBodyPart.ATTACHMENT); - mimeBodyPart.setFileName(MimeUtility.encodeText(attachmentFilename)); + mimeBodyPart.setFileName(isEncodeFilenames() ? + MimeUtility.encodeText(attachmentFilename) : attachmentFilename); mimeBodyPart.setDataHandler(new DataHandler(dataSource)); getRootMimeMultipart().addBodyPart(mimeBodyPart); } diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/commonj/TimerManagerFactoryBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/commonj/TimerManagerFactoryBean.java index 485ad6c8af1f..1d6db6c820fb 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/commonj/TimerManagerFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/commonj/TimerManagerFactoryBean.java @@ -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. @@ -16,7 +16,7 @@ package org.springframework.scheduling.commonj; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import javax.naming.NamingException; @@ -62,7 +62,8 @@ public class TimerManagerFactoryBean extends TimerManagerAccessor @Nullable private ScheduledTimerListener[] scheduledTimerListeners; - private final List timers = new LinkedList<>(); + @Nullable + private List timers; /** @@ -87,6 +88,7 @@ public void afterPropertiesSet() throws NamingException { super.afterPropertiesSet(); if (this.scheduledTimerListeners != null) { + this.timers = new ArrayList<>(this.scheduledTimerListeners.length); TimerManager timerManager = obtainTimerManager(); for (ScheduledTimerListener scheduledTask : this.scheduledTimerListeners) { Timer timer; @@ -144,15 +146,17 @@ public boolean isSingleton() { @Override public void destroy() { // Cancel all registered timers. - for (Timer timer : this.timers) { - try { - timer.cancel(); - } - catch (Throwable ex) { - logger.debug("Could not cancel CommonJ Timer", ex); + if (this.timers != null) { + for (Timer timer : this.timers) { + try { + timer.cancel(); + } + catch (Throwable ex) { + logger.debug("Could not cancel CommonJ Timer", ex); + } } + this.timers.clear(); } - this.timers.clear(); // Stop the TimerManager itself. super.destroy(); diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/commonj/WorkManagerTaskExecutor.java b/spring-context-support/src/main/java/org/springframework/scheduling/commonj/WorkManagerTaskExecutor.java index f20fde56b175..a9adcc823d5a 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/commonj/WorkManagerTaskExecutor.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/commonj/WorkManagerTaskExecutor.java @@ -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. @@ -58,13 +58,13 @@ *

The CommonJ WorkManager will usually be retrieved from the application * server's JNDI environment, as defined in the server's management console. * - *

Note: On the upcoming EE 7 compliant versions of WebLogic and WebSphere, a + *

Note: On EE 7/8 compliant versions of WebLogic and WebSphere, a * {@link org.springframework.scheduling.concurrent.DefaultManagedTaskExecutor} - * should be preferred, following JSR-236 support in Java EE 7. + * should be preferred, following JSR-236 support in Java EE 7/8. * * @author Juergen Hoeller * @since 2.0 - * @deprecated as of 5.1, in favor of EE 7's + * @deprecated as of 5.1, in favor of the EE 7/8 based * {@link org.springframework.scheduling.concurrent.DefaultManagedTaskExecutor} */ @Deprecated @@ -121,6 +121,11 @@ public void setWorkListener(WorkListener workListener) { * execution callback (which may be a wrapper around the user-supplied task). *

The primary use case is to set some execution context around the task's * invocation, or to provide some monitoring/statistics for task execution. + *

NOTE: Exception handling in {@code TaskDecorator} implementations + * is limited to plain {@code Runnable} execution via {@code execute} calls. + * In case of {@code #submit} calls, the exposed {@code Runnable} will be a + * {@code FutureTask} which does not propagate any exceptions; you might + * have to cast it and call {@code Future#get} to evaluate exceptions. * @since 4.3 */ public void setTaskDecorator(TaskDecorator taskDecorator) { diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java index d5e0af1779b4..e0b12f443f91 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/LocalDataSourceJobStore.java @@ -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. @@ -17,6 +17,7 @@ package org.springframework.scheduling.quartz; import java.sql.Connection; +import java.sql.DatabaseMetaData; import java.sql.SQLException; import javax.sql.DataSource; @@ -110,7 +111,6 @@ public Connection getConnection() throws SQLException { public void shutdown() { // Do nothing - a Spring-managed DataSource has its own lifecycle. } - /* Quartz 2.2 initialize method */ @Override public void initialize() { // Do nothing - a Spring-managed DataSource has its own lifecycle. @@ -139,7 +139,6 @@ public Connection getConnection() throws SQLException { public void shutdown() { // Do nothing - a Spring-managed DataSource has its own lifecycle. } - /* Quartz 2.2 initialize method */ @Override public void initialize() { // Do nothing - a Spring-managed DataSource has its own lifecycle. @@ -149,7 +148,8 @@ public void initialize() { // No, if HSQL is the platform, we really don't want to use locks... try { - String productName = JdbcUtils.extractDatabaseMetaData(this.dataSource, "getDatabaseProductName"); + String productName = JdbcUtils.extractDatabaseMetaData(this.dataSource, + DatabaseMetaData::getDatabaseProductName); productName = JdbcUtils.commonDatabaseName(productName); if (productName != null && productName.toLowerCase().contains("hsql")) { setUseDBLocks(false); diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/ResourceLoaderClassLoadHelper.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/ResourceLoaderClassLoadHelper.java index 1b274b79f316..996a598460d2 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/ResourceLoaderClassLoadHelper.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/ResourceLoaderClassLoadHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -81,8 +81,8 @@ public Class loadClass(String name) throws ClassNotFoundException { return ClassUtils.forName(name, this.resourceLoader.getClassLoader()); } - @Override @SuppressWarnings("unchecked") + @Override public Class loadClass(String name, Class clazz) throws ClassNotFoundException { return (Class) loadClass(name); } diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java index 434cd49a3b11..de60cbb9ca5f 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SchedulerAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -228,7 +227,7 @@ protected void registerJobsAndTriggers() throws SchedulerException { } else { // Create empty list for easier checks when registering triggers. - this.jobDetails = new LinkedList<>(); + this.jobDetails = new ArrayList<>(); } // Register Calendars. diff --git a/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerTemplateUtils.java b/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerTemplateUtils.java index 2c9fa7e4781b..e1ca06bf99e3 100644 --- a/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerTemplateUtils.java +++ b/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerTemplateUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 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. @@ -46,7 +46,7 @@ public abstract class FreeMarkerTemplateUtils { public static String processTemplateIntoString(Template template, Object model) throws IOException, TemplateException { - StringWriter result = new StringWriter(); + StringWriter result = new StringWriter(1024); template.process(model, result); return result.toString(); } diff --git a/spring-context-support/src/main/resources/.gitignore b/spring-context-support/src/main/resources/.gitignore deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/spring-context-support/src/test/java/org/springframework/cache/caffeine/CaffeineCacheManagerTests.java b/spring-context-support/src/test/java/org/springframework/cache/caffeine/CaffeineCacheManagerTests.java index 7637e1054542..e90a3ece9363 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/caffeine/CaffeineCacheManagerTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/caffeine/CaffeineCacheManagerTests.java @@ -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. @@ -125,7 +125,7 @@ public void changeCaffeineRecreateCache() { Cache cache1x = cm.getCache("c1"); assertThat(cache1x != cache1).isTrue(); - cm.setCaffeine(caffeine); // Set same instance + cm.setCaffeine(caffeine); // Set same instance Cache cache1xx = cm.getCache("c1"); assertThat(cache1xx).isSameAs(cache1x); } @@ -155,12 +155,14 @@ public void changeCacheLoaderRecreateCache() { CaffeineCacheManager cm = new CaffeineCacheManager("c1"); Cache cache1 = cm.getCache("c1"); - CacheLoader loader = mockCacheLoader(); + @SuppressWarnings("unchecked") + CacheLoader loader = mock(CacheLoader.class); + cm.setCacheLoader(loader); Cache cache1x = cm.getCache("c1"); assertThat(cache1x != cache1).isTrue(); - cm.setCacheLoader(loader); // Set same instance + cm.setCacheLoader(loader); // Set same instance Cache cache1xx = cm.getCache("c1"); assertThat(cache1xx).isSameAs(cache1x); } @@ -194,9 +196,19 @@ public Object load(Object key) throws Exception { .withMessageContaining("I only know ping"); } - @SuppressWarnings("unchecked") - private CacheLoader mockCacheLoader() { - return mock(CacheLoader.class); + @Test + public void customCacheRegistration() { + CaffeineCacheManager cm = new CaffeineCacheManager("c1"); + com.github.benmanes.caffeine.cache.Cache nc = Caffeine.newBuilder().build(); + cm.registerCustomCache("c2", nc); + + Cache cache1 = cm.getCache("c1"); + Cache cache2 = cm.getCache("c2"); + assertThat(nc == cache2.getNativeCache()).isTrue(); + + cm.setCaffeine(Caffeine.newBuilder().maximumSize(10)); + assertThat(cm.getCache("c1") != cache1).isTrue(); + assertThat(cm.getCache("c2") == cache2).isTrue(); } } diff --git a/spring-context-support/src/test/java/org/springframework/cache/caffeine/CaffeineCacheTests.java b/spring-context-support/src/test/java/org/springframework/cache/caffeine/CaffeineCacheTests.java index eae5f14bb449..215720353304 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/caffeine/CaffeineCacheTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/caffeine/CaffeineCacheTests.java @@ -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. @@ -20,16 +20,20 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.cache.AbstractValueAdaptingCacheTests; import org.springframework.cache.Cache; +import org.springframework.cache.Cache.ValueWrapper; +import org.springframework.context.testfixture.cache.AbstractValueAdaptingCacheTests; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; /** + * Tests for {@link CaffeineCache}. + * * @author Ben Manes * @author Stephane Nicoll */ -public class CaffeineCacheTests extends AbstractValueAdaptingCacheTests { +class CaffeineCacheTests extends AbstractValueAdaptingCacheTests { private com.github.benmanes.caffeine.cache.Cache nativeCache; @@ -38,12 +42,12 @@ public class CaffeineCacheTests extends AbstractValueAdaptingCacheTests nativeCacheNoNull = Caffeine.newBuilder().build(); - cacheNoNull = new CaffeineCache(CACHE_NAME_NO_NULL, nativeCacheNoNull, false); + cacheNoNull = new CaffeineCache(CACHE_NAME_NO_NULL, nativeCacheNoNull, false); } @Override @@ -62,7 +66,35 @@ protected Object getNativeCache() { } @Test - public void testPutIfAbsentNullValue() throws Exception { + void testLoadingCacheGet() { + Object value = new Object(); + CaffeineCache loadingCache = new CaffeineCache(CACHE_NAME, Caffeine.newBuilder() + .build(key -> value)); + ValueWrapper valueWrapper = loadingCache.get(new Object()); + assertThat(valueWrapper).isNotNull(); + assertThat(valueWrapper.get()).isEqualTo(value); + } + + @Test + void testLoadingCacheGetWithType() { + String value = "value"; + CaffeineCache loadingCache = new CaffeineCache(CACHE_NAME, Caffeine.newBuilder() + .build(key -> value)); + String valueWrapper = loadingCache.get(new Object(), String.class); + assertThat(valueWrapper).isNotNull(); + assertThat(valueWrapper).isEqualTo(value); + } + + @Test + void testLoadingCacheGetWithWrongType() { + String value = "value"; + CaffeineCache loadingCache = new CaffeineCache(CACHE_NAME, Caffeine.newBuilder() + .build(key -> value)); + assertThatIllegalStateException().isThrownBy(() -> loadingCache.get(new Object(), Long.class)); + } + + @Test + void testPutIfAbsentNullValue() { CaffeineCache cache = getCache(); Object key = new Object(); diff --git a/spring-context-support/src/test/java/org/springframework/cache/ehcache/EhCacheCacheTests.java b/spring-context-support/src/test/java/org/springframework/cache/ehcache/EhCacheCacheTests.java index df0e02b72ae7..b9bb1cc8542a 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/ehcache/EhCacheCacheTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/ehcache/EhCacheCacheTests.java @@ -25,11 +25,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.cache.AbstractCacheTests; -import org.springframework.tests.EnabledForTestGroups; +import org.springframework.context.testfixture.cache.AbstractCacheTests; +import org.springframework.core.testfixture.EnabledForTestGroups; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.tests.TestGroup.LONG_RUNNING; +import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING; /** * @author Costin Leau diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheEhCacheAnnotationTests.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheEhCacheAnnotationTests.java index 1c0031854174..188cc293c974 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheEhCacheAnnotationTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheEhCacheAnnotationTests.java @@ -28,19 +28,19 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; -import org.springframework.cache.config.AbstractCacheAnnotationTests; -import org.springframework.cache.config.AnnotatedClassCacheableService; -import org.springframework.cache.config.CacheableService; -import org.springframework.cache.config.DefaultCacheableService; -import org.springframework.cache.config.SomeCustomKeyGenerator; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.SimpleKeyGenerator; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.tests.transaction.CallCountingTransactionManager; +import org.springframework.context.testfixture.cache.AbstractCacheAnnotationTests; +import org.springframework.context.testfixture.cache.SomeCustomKeyGenerator; +import org.springframework.context.testfixture.cache.beans.AnnotatedClassCacheableService; +import org.springframework.context.testfixture.cache.beans.CacheableService; +import org.springframework.context.testfixture.cache.beans.DefaultCacheableService; import org.springframework.transaction.support.TransactionTemplate; +import org.springframework.transaction.testfixture.CallCountingTransactionManager; /** * @author Stephane Nicoll diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheEhCacheApiTests.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheEhCacheApiTests.java index d0797dd92696..b826e2a3fb37 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheEhCacheApiTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/JCacheEhCacheApiTests.java @@ -25,7 +25,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.springframework.cache.AbstractValueAdaptingCacheTests; +import org.springframework.context.testfixture.cache.AbstractValueAdaptingCacheTests; /** * @author Stephane Nicoll diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheCustomInterceptorTests.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheCustomInterceptorTests.java index 8ac2f4a3e6b0..6d5714d09574 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheCustomInterceptorTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheCustomInterceptorTests.java @@ -37,6 +37,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.contextsupport.testfixture.jcache.JCacheableService; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheJavaConfigTests.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheJavaConfigTests.java index 60555a7a3e76..5c12aceac022 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheJavaConfigTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheJavaConfigTests.java @@ -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. @@ -25,7 +25,6 @@ import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.concurrent.ConcurrentMapCache; import org.springframework.cache.concurrent.ConcurrentMapCacheManager; -import org.springframework.cache.config.SomeKeyGenerator; import org.springframework.cache.interceptor.CacheErrorHandler; import org.springframework.cache.interceptor.CacheResolver; import org.springframework.cache.interceptor.KeyGenerator; @@ -43,6 +42,9 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.testfixture.cache.SomeKeyGenerator; +import org.springframework.contextsupport.testfixture.jcache.AbstractJCacheAnnotationTests; +import org.springframework.contextsupport.testfixture.jcache.JCacheableService; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; @@ -99,10 +101,7 @@ public void bothSetOnlyResolverIsUsed() { @Test public void exceptionCacheResolverLazilyRequired() { - ConfigurableApplicationContext context = - new AnnotationConfigApplicationContext(NoExceptionCacheResolverConfig.class); - - try { + try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(NoExceptionCacheResolverConfig.class)) { DefaultJCacheOperationSource cos = context.getBean(DefaultJCacheOperationSource.class); assertThat(cos.getCacheResolver()).isSameAs(context.getBean("cacheResolver")); @@ -113,9 +112,6 @@ public void exceptionCacheResolverLazilyRequired() { assertThatIllegalStateException().isThrownBy(() -> service.cacheWithException("test", false)); } - finally { - context.close(); - } } diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheNamespaceDrivenTests.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheNamespaceDrivenTests.java index 28f7c513c76d..262689f83ad8 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheNamespaceDrivenTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheNamespaceDrivenTests.java @@ -24,6 +24,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; +import org.springframework.contextsupport.testfixture.jcache.AbstractJCacheAnnotationTests; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheStandaloneConfigTests.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheStandaloneConfigTests.java index c57d7ecc2134..f07e5214850e 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheStandaloneConfigTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheStandaloneConfigTests.java @@ -18,6 +18,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; +import org.springframework.contextsupport.testfixture.jcache.AbstractJCacheAnnotationTests; /** * @author Stephane Nicoll diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/AnnotatedJCacheableService.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/AnnotatedJCacheableService.java index 1e44ab251d02..e5cfb12b64e6 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/AnnotatedJCacheableService.java +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/AnnotatedJCacheableService.java @@ -30,9 +30,9 @@ import org.springframework.cache.Cache; import org.springframework.cache.interceptor.SimpleKeyGenerator; -import org.springframework.cache.jcache.config.JCacheableService; -import org.springframework.cache.jcache.support.TestableCacheKeyGenerator; -import org.springframework.cache.jcache.support.TestableCacheResolverFactory; +import org.springframework.contextsupport.testfixture.cache.TestableCacheKeyGenerator; +import org.springframework.contextsupport.testfixture.cache.TestableCacheResolverFactory; +import org.springframework.contextsupport.testfixture.jcache.JCacheableService; /** * Repository sample with a @CacheDefaults annotation diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/AnnotationCacheOperationSourceTests.java b/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/AnnotationCacheOperationSourceTests.java index 6a2e6d756b01..bcc9276243bd 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/AnnotationCacheOperationSourceTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/jcache/interceptor/AnnotationCacheOperationSourceTests.java @@ -32,9 +32,9 @@ import org.springframework.cache.interceptor.CacheResolver; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.jcache.AbstractJCacheTests; -import org.springframework.cache.jcache.support.TestableCacheKeyGenerator; -import org.springframework.cache.jcache.support.TestableCacheResolver; -import org.springframework.cache.jcache.support.TestableCacheResolverFactory; +import org.springframework.contextsupport.testfixture.cache.TestableCacheKeyGenerator; +import org.springframework.contextsupport.testfixture.cache.TestableCacheResolver; +import org.springframework.contextsupport.testfixture.cache.TestableCacheResolverFactory; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; diff --git a/spring-context-support/src/test/java/org/springframework/cache/transaction/TransactionAwareCacheDecoratorTests.java b/spring-context-support/src/test/java/org/springframework/cache/transaction/TransactionAwareCacheDecoratorTests.java index 74337eb76e48..66369edfe6c0 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/transaction/TransactionAwareCacheDecoratorTests.java +++ b/spring-context-support/src/test/java/org/springframework/cache/transaction/TransactionAwareCacheDecoratorTests.java @@ -20,8 +20,8 @@ import org.springframework.cache.Cache; import org.springframework.cache.concurrent.ConcurrentMapCache; -import org.springframework.tests.transaction.CallCountingTransactionManager; import org.springframework.transaction.support.TransactionTemplate; +import org.springframework.transaction.testfixture.CallCountingTransactionManager; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; diff --git a/spring-context-support/src/test/java/org/springframework/scheduling/quartz/QuartzSupportTests.java b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/QuartzSupportTests.java index b2503fb7db35..6c7d5b8691c7 100644 --- a/spring-context-support/src/test/java/org/springframework/scheduling/quartz/QuartzSupportTests.java +++ b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/QuartzSupportTests.java @@ -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. @@ -34,19 +34,19 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.StaticApplicationContext; import org.springframework.core.task.TaskExecutor; +import org.springframework.core.testfixture.EnabledForTestGroups; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.tests.EnabledForTestGroups; -import org.springframework.tests.sample.beans.TestBean; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.springframework.tests.TestGroup.PERFORMANCE; +import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING; /** * @author Juergen Hoeller @@ -96,7 +96,7 @@ protected Scheduler createScheduler(SchedulerFactory schedulerFactory, String sc } @Test - @EnabledForTestGroups(PERFORMANCE) + @EnabledForTestGroups(LONG_RUNNING) public void schedulerWithTaskExecutor() throws Exception { CountingTaskExecutor taskExecutor = new CountingTaskExecutor(); DummyJob.count = 0; @@ -137,7 +137,7 @@ public void jobDetailWithRunnableInsteadOfJob() { } @Test - @EnabledForTestGroups(PERFORMANCE) + @EnabledForTestGroups(LONG_RUNNING) public void schedulerWithQuartzJobBean() throws Exception { DummyJob.param = 0; DummyJob.count = 0; @@ -170,7 +170,7 @@ public void schedulerWithQuartzJobBean() throws Exception { } @Test - @EnabledForTestGroups(PERFORMANCE) + @EnabledForTestGroups(LONG_RUNNING) public void schedulerWithSpringBeanJobFactory() throws Exception { DummyJob.param = 0; DummyJob.count = 0; @@ -205,7 +205,7 @@ public void schedulerWithSpringBeanJobFactory() throws Exception { } @Test - @EnabledForTestGroups(PERFORMANCE) + @EnabledForTestGroups(LONG_RUNNING) public void schedulerWithSpringBeanJobFactoryAndParamMismatchNotIgnored() throws Exception { DummyJob.param = 0; DummyJob.count = 0; @@ -241,7 +241,7 @@ public void schedulerWithSpringBeanJobFactoryAndParamMismatchNotIgnored() throws } @Test - @EnabledForTestGroups(PERFORMANCE) + @EnabledForTestGroups(LONG_RUNNING) public void schedulerWithSpringBeanJobFactoryAndQuartzJobBean() throws Exception { DummyJobBean.param = 0; DummyJobBean.count = 0; @@ -275,7 +275,7 @@ public void schedulerWithSpringBeanJobFactoryAndQuartzJobBean() throws Exception } @Test - @EnabledForTestGroups(PERFORMANCE) + @EnabledForTestGroups(LONG_RUNNING) public void schedulerWithSpringBeanJobFactoryAndJobSchedulingData() throws Exception { DummyJob.param = 0; DummyJob.count = 0; @@ -295,40 +295,31 @@ public void schedulerWithSpringBeanJobFactoryAndJobSchedulingData() throws Excep @Test // SPR-772 public void multipleSchedulers() throws Exception { - ClassPathXmlApplicationContext ctx = context("multipleSchedulers.xml"); - try { + try (ClassPathXmlApplicationContext ctx = context("multipleSchedulers.xml")) { Scheduler scheduler1 = (Scheduler) ctx.getBean("scheduler1"); Scheduler scheduler2 = (Scheduler) ctx.getBean("scheduler2"); assertThat(scheduler2).isNotSameAs(scheduler1); assertThat(scheduler1.getSchedulerName()).isEqualTo("quartz1"); assertThat(scheduler2.getSchedulerName()).isEqualTo("quartz2"); } - finally { - ctx.close(); - } } @Test // SPR-16884 public void multipleSchedulersWithQuartzProperties() throws Exception { - ClassPathXmlApplicationContext ctx = context("multipleSchedulersWithQuartzProperties.xml"); - try { + try (ClassPathXmlApplicationContext ctx = context("multipleSchedulersWithQuartzProperties.xml")) { Scheduler scheduler1 = (Scheduler) ctx.getBean("scheduler1"); Scheduler scheduler2 = (Scheduler) ctx.getBean("scheduler2"); assertThat(scheduler2).isNotSameAs(scheduler1); assertThat(scheduler1.getSchedulerName()).isEqualTo("quartz1"); assertThat(scheduler2.getSchedulerName()).isEqualTo("quartz2"); } - finally { - ctx.close(); - } } @Test - @EnabledForTestGroups(PERFORMANCE) + @EnabledForTestGroups(LONG_RUNNING) public void twoAnonymousMethodInvokingJobDetailFactoryBeans() throws Exception { - ClassPathXmlApplicationContext ctx = context("multipleAnonymousMethodInvokingJobDetailFB.xml"); Thread.sleep(3000); - try { + try (ClassPathXmlApplicationContext ctx = context("multipleAnonymousMethodInvokingJobDetailFB.xml")) { QuartzTestBean exportService = (QuartzTestBean) ctx.getBean("exportService"); QuartzTestBean importService = (QuartzTestBean) ctx.getBean("importService"); @@ -337,17 +328,13 @@ public void twoAnonymousMethodInvokingJobDetailFactoryBeans() throws Exception { assertThat(importService.getImportCount()).as("doImport not called on importService").isEqualTo(2); assertThat(importService.getExportCount()).as("doExport called on importService").isEqualTo(0); } - finally { - ctx.close(); - } } @Test - @EnabledForTestGroups(PERFORMANCE) + @EnabledForTestGroups(LONG_RUNNING) public void schedulerAccessorBean() throws Exception { - ClassPathXmlApplicationContext ctx = context("schedulerAccessorBean.xml"); Thread.sleep(3000); - try { + try (ClassPathXmlApplicationContext ctx = context("schedulerAccessorBean.xml")) { QuartzTestBean exportService = (QuartzTestBean) ctx.getBean("exportService"); QuartzTestBean importService = (QuartzTestBean) ctx.getBean("importService"); @@ -356,9 +343,6 @@ public void schedulerAccessorBean() throws Exception { assertThat(importService.getImportCount()).as("doImport not called on importService").isEqualTo(2); assertThat(importService.getExportCount()).as("doExport called on importService").isEqualTo(0); } - finally { - ctx.close(); - } } @Test @@ -387,9 +371,9 @@ public void schedulerAutoStartupFalse() throws Exception { @Test public void schedulerRepositoryExposure() throws Exception { - ClassPathXmlApplicationContext ctx = context("schedulerRepositoryExposure.xml"); - assertThat(ctx.getBean("scheduler")).isSameAs(SchedulerRepository.getInstance().lookup("myScheduler")); - ctx.close(); + try (ClassPathXmlApplicationContext ctx = context("schedulerRepositoryExposure.xml")) { + assertThat(ctx.getBean("scheduler")).isSameAs(SchedulerRepository.getInstance().lookup("myScheduler")); + } } /** @@ -401,19 +385,15 @@ public void schedulerWithHsqlDataSource() throws Exception { DummyJob.param = 0; DummyJob.count = 0; - ClassPathXmlApplicationContext ctx = context("databasePersistence.xml"); - JdbcTemplate jdbcTemplate = new JdbcTemplate(ctx.getBean(DataSource.class)); - assertThat(jdbcTemplate.queryForList("SELECT * FROM qrtz_triggers").isEmpty()).as("No triggers were persisted").isFalse(); + try (ClassPathXmlApplicationContext ctx = context("databasePersistence.xml")) { + JdbcTemplate jdbcTemplate = new JdbcTemplate(ctx.getBean(DataSource.class)); + assertThat(jdbcTemplate.queryForList("SELECT * FROM qrtz_triggers").isEmpty()).as("No triggers were persisted").isFalse(); - /* - Thread.sleep(3000); - try { - assertTrue("DummyJob should have been executed at least once.", DummyJob.count > 0); - } - finally { - ctx.close(); + /* + Thread.sleep(3000); + assertTrue("DummyJob should have been executed at least once.", DummyJob.count > 0); + */ } - */ } private ClassPathXmlApplicationContext context(String path) { diff --git a/spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/BeanValidationPostProcessorTests.java b/spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/BeanValidationPostProcessorTests.java index 0f6e099f6f48..c5da1e4b1521 100644 --- a/spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/BeanValidationPostProcessorTests.java +++ b/spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/BeanValidationPostProcessorTests.java @@ -24,9 +24,9 @@ import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.testfixture.beans.TestBean; import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor; import org.springframework.context.support.GenericApplicationContext; -import org.springframework.tests.sample.beans.TestBean; import org.springframework.validation.beanvalidation.BeanValidationPostProcessor; import static org.assertj.core.api.Assertions.assertThat; @@ -43,9 +43,10 @@ public void testNotNullConstraint() { ac.registerBeanDefinition("bvpp", new RootBeanDefinition(BeanValidationPostProcessor.class)); ac.registerBeanDefinition("capp", new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class)); ac.registerBeanDefinition("bean", new RootBeanDefinition(NotNullConstrainedBean.class)); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy( - ac::refresh) - .satisfies(ex -> assertThat(ex.getRootCause().getMessage()).contains("testBean", "invalid")); + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(ac::refresh) + .havingRootCause() + .withMessageContainingAll("testBean", "invalid"); ac.close(); } @@ -81,9 +82,10 @@ public void testSizeConstraint() { bd.getPropertyValues().add("testBean", new TestBean()); bd.getPropertyValues().add("stringValue", "s"); ac.registerBeanDefinition("bean", bd); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy( - ac::refresh) - .satisfies(ex -> assertThat(ex.getRootCause().getMessage()).contains("stringValue", "invalid")); + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(ac::refresh) + .havingRootCause() + .withMessageContainingAll("stringValue", "invalid"); ac.close(); } diff --git a/spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/SpringValidatorAdapterTests.java b/spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/SpringValidatorAdapterTests.java index 7639b6704b48..813111adb05c 100644 --- a/spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/SpringValidatorAdapterTests.java +++ b/spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/SpringValidatorAdapterTests.java @@ -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. @@ -28,7 +28,6 @@ import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashSet; -import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -52,8 +51,8 @@ import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.context.support.StaticMessageSource; +import org.springframework.core.testfixture.io.SerializationTestUtils; import org.springframework.util.ObjectUtils; -import org.springframework.util.SerializationTestUtils; import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.FieldError; import org.springframework.validation.beanvalidation.SpringValidatorAdapter; @@ -406,7 +405,7 @@ public static class Parent { private Set childSet = new LinkedHashSet<>(); @Valid - private List childList = new LinkedList<>(); + private List childList = new ArrayList<>(); public Integer getId() { return id; diff --git a/spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/ValidatorFactoryTests.java b/spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/ValidatorFactoryTests.java index 77e473dc084e..a383ae5f8bf7 100644 --- a/spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/ValidatorFactoryTests.java +++ b/spring-context-support/src/test/java/org/springframework/validation/beanvalidation2/ValidatorFactoryTests.java @@ -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. @@ -20,10 +20,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedHashSet; -import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.Set; @@ -295,7 +295,7 @@ public static class ValidPerson { private ValidAddress address = new ValidAddress(); @Valid - private List addressList = new LinkedList<>(); + private List addressList = new ArrayList<>(); @Valid private Set addressSet = new LinkedHashSet<>(); @@ -457,7 +457,7 @@ public boolean isValid(InnerBean bean, ConstraintValidatorContext context) { public static class ListContainer { @NotXList - private List list = new LinkedList<>(); + private List list = new ArrayList<>(); public void addString(String value) { list.add(value); diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/support/TestableCacheKeyGenerator.java b/spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/cache/TestableCacheKeyGenerator.java similarity index 96% rename from spring-context-support/src/test/java/org/springframework/cache/jcache/support/TestableCacheKeyGenerator.java rename to spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/cache/TestableCacheKeyGenerator.java index 3e881dd519e0..9bb225ba80f9 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/support/TestableCacheKeyGenerator.java +++ b/spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/cache/TestableCacheKeyGenerator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cache.jcache.support; +package org.springframework.contextsupport.testfixture.cache; import java.lang.annotation.Annotation; diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/support/TestableCacheResolver.java b/spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/cache/TestableCacheResolver.java similarity index 95% rename from spring-context-support/src/test/java/org/springframework/cache/jcache/support/TestableCacheResolver.java rename to spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/cache/TestableCacheResolver.java index 519ba73422de..04567b1a8459 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/support/TestableCacheResolver.java +++ b/spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/cache/TestableCacheResolver.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cache.jcache.support; +package org.springframework.contextsupport.testfixture.cache; import java.lang.annotation.Annotation; diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/support/TestableCacheResolverFactory.java b/spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/cache/TestableCacheResolverFactory.java similarity index 95% rename from spring-context-support/src/test/java/org/springframework/cache/jcache/support/TestableCacheResolverFactory.java rename to spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/cache/TestableCacheResolverFactory.java index c544dde8bbe9..5849090acd1b 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/support/TestableCacheResolverFactory.java +++ b/spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/cache/TestableCacheResolverFactory.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cache.jcache.support; +package org.springframework.contextsupport.testfixture.cache; import java.lang.annotation.Annotation; diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/AbstractJCacheAnnotationTests.java b/spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/jcache/AbstractJCacheAnnotationTests.java similarity index 99% rename from spring-context-support/src/test/java/org/springframework/cache/jcache/config/AbstractJCacheAnnotationTests.java rename to spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/jcache/AbstractJCacheAnnotationTests.java index aa6478acec69..db5bb038e94f 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/AbstractJCacheAnnotationTests.java +++ b/spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/jcache/AbstractJCacheAnnotationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cache.jcache.config; +package org.springframework.contextsupport.testfixture.jcache; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; diff --git a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheableService.java b/spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/jcache/JCacheableService.java similarity index 96% rename from spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheableService.java rename to spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/jcache/JCacheableService.java index 1c43cfbea2e5..db36d1103207 100644 --- a/spring-context-support/src/test/java/org/springframework/cache/jcache/config/JCacheableService.java +++ b/spring-context-support/src/testFixtures/java/org/springframework/contextsupport/testfixture/jcache/JCacheableService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.cache.jcache.config; +package org.springframework.contextsupport.testfixture.jcache; import java.io.IOException; diff --git a/spring-context/spring-context.gradle b/spring-context/spring-context.gradle index 54ee48da69ac..0b06a7c56f0f 100644 --- a/spring-context/spring-context.gradle +++ b/spring-context/spring-context.gradle @@ -15,29 +15,35 @@ dependencies { optional("javax.inject:javax.inject") optional("javax.interceptor:javax.interceptor-api") optional("javax.money:money-api") - // TODO: overriding 2.0.1.Final, because of LocalValidatorFactoryBean + // Overriding 2.0.1.Final due to Bean Validation 1.1 compatibility in LocalValidatorFactoryBean optional("javax.validation:validation-api:1.1.0.Final") optional("javax.xml.ws:jaxws-api") optional("org.aspectj:aspectjweaver") optional("org.codehaus.groovy:groovy") - optional("org.beanshell:bsh") + optional("org.apache-extras.beanshell:bsh") optional("joda-time:joda-time") optional("org.hibernate:hibernate-validator:5.4.3.Final") optional("org.jetbrains.kotlin:kotlin-reflect") optional("org.jetbrains.kotlin:kotlin-stdlib") optional("org.reactivestreams:reactive-streams") + testCompile(testFixtures(project(":spring-aop"))) + testCompile(testFixtures(project(":spring-beans"))) + testCompile(testFixtures(project(":spring-core"))) testCompile("io.projectreactor:reactor-core") testCompile("org.codehaus.groovy:groovy-jsr223") testCompile("org.codehaus.groovy:groovy-test") testCompile("org.codehaus.groovy:groovy-xml") testCompile("org.apache.commons:commons-pool2") testCompile("javax.inject:javax.inject-tck") - // Substitute for "javax.management:jmxremote_optional:1.0.1_04" which - // is not available on Maven Central - testRuntime("org.glassfish.external:opendmk_jmxremote_optional_jar") testCompile("org.awaitility:awaitility") testRuntime("javax.xml.bind:jaxb-api") testRuntime("org.glassfish:javax.el") + // Substitute for javax.management:jmxremote_optional:1.0.1_04 (not available on Maven Central) + testRuntime("org.glassfish.external:opendmk_jmxremote_optional_jar") testRuntime("org.javamoney:moneta") testRuntime("org.junit.vintage:junit-vintage-engine") // for @Inject TCK + testFixturesApi("org.junit.jupiter:junit-jupiter-api") + testFixturesImplementation(testFixtures(project(":spring-beans"))) + testFixturesImplementation("com.google.code.findbugs:jsr305") + testFixturesImplementation("org.assertj:assertj-core") } diff --git a/spring-context/src/jmh/java/org/springframework/context/annotation/AnnotationProcessorBenchmark.java b/spring-context/src/jmh/java/org/springframework/context/annotation/AnnotationProcessorBenchmark.java new file mode 100644 index 000000000000..c00cf0ddfaec --- /dev/null +++ b/spring-context/src/jmh/java/org/springframework/context/annotation/AnnotationProcessorBenchmark.java @@ -0,0 +1,105 @@ +/* + * 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.context.annotation; + +import javax.annotation.Resource; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.testfixture.beans.ITestBean; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.context.support.GenericApplicationContext; + +/** + * Benchmark for bean annotation processing with various annotations. + * @author Brian Clozel + */ +@BenchmarkMode(Mode.Throughput) +public class AnnotationProcessorBenchmark { + + @State(Scope.Benchmark) + public static class BenchmarkState { + + public GenericApplicationContext context; + + @Param({"ResourceAnnotatedTestBean", "AutowiredAnnotatedTestBean"}) + public String testBeanClass; + + @Param({"true", "false"}) + public boolean overridden; + + @Setup + public void setup() { + RootBeanDefinition rbd; + this.context = new GenericApplicationContext(); + AnnotationConfigUtils.registerAnnotationConfigProcessors(this.context); + this.context.refresh(); + if (this.testBeanClass.equals("ResourceAnnotatedTestBean")) { + rbd = new RootBeanDefinition(ResourceAnnotatedTestBean.class); + } + else { + rbd = new RootBeanDefinition(AutowiredAnnotatedTestBean.class); + } + rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE); + if (this.overridden) { + rbd.getPropertyValues().add("spouse", new RuntimeBeanReference("spouse")); + } + this.context.registerBeanDefinition("test", rbd); + this.context.registerBeanDefinition("spouse", new RootBeanDefinition(TestBean.class)); + } + } + + @Benchmark + public ITestBean prototypeCreation(BenchmarkState state) { + TestBean tb = state.context.getBean("test", TestBean.class); + return tb.getSpouse(); + } + + + private static class ResourceAnnotatedTestBean extends org.springframework.beans.testfixture.beans.TestBean { + + @Override + @Resource + @SuppressWarnings("deprecation") + @org.springframework.beans.factory.annotation.Required + public void setSpouse(ITestBean spouse) { + super.setSpouse(spouse); + } + } + + private static class AutowiredAnnotatedTestBean extends TestBean { + + @Override + @Autowired + @SuppressWarnings("deprecation") + @org.springframework.beans.factory.annotation.Required + public void setSpouse(ITestBean spouse) { + super.setSpouse(spouse); + } + } + +} diff --git a/spring-context/src/jmh/java/org/springframework/context/expression/ApplicationContextExpressionBenchmark.java b/spring-context/src/jmh/java/org/springframework/context/expression/ApplicationContextExpressionBenchmark.java new file mode 100644 index 000000000000..51a568ca30a1 --- /dev/null +++ b/spring-context/src/jmh/java/org/springframework/context/expression/ApplicationContextExpressionBenchmark.java @@ -0,0 +1,71 @@ +/* + * 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.context.expression; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.infra.Blackhole; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.context.support.GenericApplicationContext; + +/** + * Benchmark for application context expressions resolution during prototype bean creation. + * @author Brian Clozel + */ +@BenchmarkMode(Mode.Throughput) +public class ApplicationContextExpressionBenchmark { + + @State(Scope.Benchmark) + public static class BenchmarkState { + + public GenericApplicationContext context; + + @Setup + public void setup() { + System.getProperties().put("name", "juergen"); + System.getProperties().put("country", "UK"); + this.context = new GenericApplicationContext(); + RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); + rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE); + rbd.getConstructorArgumentValues().addGenericArgumentValue("#{systemProperties.name}"); + rbd.getPropertyValues().add("country", "#{systemProperties.country}"); + this.context.registerBeanDefinition("test", rbd); + this.context.refresh(); + } + + @TearDown + public void teardown() { + System.getProperties().remove("country"); + System.getProperties().remove("name"); + } + } + + @Benchmark + public void prototypeCreationWithSystemProperties(BenchmarkState state, Blackhole bh) { + TestBean tb = (TestBean) state.context.getBean("test"); + bh.consume(tb.getName()); + bh.consume(tb.getCountry()); + } +} diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java b/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java index 07d8394c38ac..c49d8d20c6e0 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java @@ -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. @@ -41,7 +41,7 @@ * @since 3.1 * @see EnableCaching */ -@Configuration +@Configuration(proxyBeanMethods = false) public abstract class AbstractCachingConfiguration implements ImportAware { @Nullable diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/CachePut.java b/spring-context/src/main/java/org/springframework/cache/annotation/CachePut.java index 8743df7e668b..abf47bfd0fa1 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/CachePut.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/CachePut.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,8 @@ * *

In contrast to the {@link Cacheable @Cacheable} annotation, this annotation * does not cause the advised method to be skipped. Rather, it always causes the - * method to be invoked and its result to be stored in the associated cache. Note + * method to be invoked and its result to be stored in the associated cache if the + * {@link #condition()} and {@link #unless()} expressions match accordingly. Note * that Java8's {@code Optional} return types are automatically handled and its * content is stored in the cache if present. * @@ -118,10 +119,15 @@ /** * Spring Expression Language (SpEL) expression used for making the cache * put operation conditional. + *

This expression is evaluated after the method has been called due to the + * nature of the put operation and can therefore refer to the {@code result}. *

Default is {@code ""}, meaning the method result is always cached. *

The SpEL expression evaluates against a dedicated context that provides the * following meta-data: *