diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000000..5cdaa4ba7117 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,17 @@ + +**Affects:** \ + +--- + \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8679c09c6cc0..da7d06518a58 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,7 +29,7 @@ If you have a question, check StackOverflow using Find an existing discussion or start a new one if necessary. If you suspect an issue, perform a search in the -[JIRA issue tracker](https://jira.spring.io/browse/SPR), using a few different keywords. +[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. @@ -42,7 +42,7 @@ Before you create a ticket, please take the time to [research first](#discuss). If creating a ticket after a discussion on StackOverflow, please provide a self-sufficient description in the ticket, independent of the details on StackOverview. 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. -When ready create a ticket in the [JIRA issue tracker](https://jira.spring.io/browse/SPR). +When ready create a ticket in the [GitHub issue tracker](https://github.com/spring-projects/spring-framework/issues). #### Ticket Lifecycle @@ -66,7 +66,7 @@ automatically when you submit a pull request. 1. For all but the most trivial of contributions, please [create a ticket](#create-a-ticket). The purpose of the ticket is to understand and discuss the underlying issue or feature. -We use the JIRA issue tracker as the preferred place of record for conversations and +We use the GitHub issue tracker as the preferred place of record for conversations and conclusions. In that sense discussions directly under a PR are more implementation detail oriented and transient in nature. @@ -75,8 +75,8 @@ oriented and transient in nature. 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 JIRA issue (e.g. `SPR-1234`), or -otherwise using succinct, lower-case, dash (-) delimited names, such as `fix-warnings'. +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 @@ -84,12 +84,12 @@ multiple edits or corrections of the same logical change. See for an overview of streamlining commit history. 1. Format commit messages using 55 characters for the subject line, 72 lines for the -description, followed by related issues, e.g. `Issues: SPR-1234, SPR-1235`. +description, followed by the issue fixed, e.g. `Fixes #22276`. See the [Commit Guidelines section of Pro Git](http://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 JIRA issue number in the PR description. +1. List the GitHub issue number in the PR description. 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 @@ -116,7 +116,7 @@ defines the source file coding standards we use along with some IDEA editor sett The reference documentation is in the [src/docs/asciidoc](src/docs/asciidoc) directory and, in [Asciidoctor](http://asciidoctor.org/) format. For trivial changes, you may be able to browse, -edit source files, and submit directly from Github. +edit source files, and submit directly from GitHub. When making changes locally, use `./gradlew asciidoctor` and then browse the result under `build/asciidoc/html5/index.html`. diff --git a/README.md b/README.md index 6e0d5408b3bc..1d96e81702ce 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,30 @@ # Spring Framework -This is the home of the Spring Framework, the foundation for all -[Spring projects](https://spring.io/projects). Together the Spring Framework and the family of Spring projects make up what we call "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 is often referred to simply as "Spring". -Spring provides everything you need beyond the Java language to create enterprise -applications in a wide range of scenarios and architectures. Please read the -[Overview](https://docs.spring.io/spring/docs/current/spring-framework-reference/overview.html#spring-introduction) -section in the reference for a more complete introduction. +Spring provides everything required beyond the Java programming language for creating enterprise applications for a wide range of scenarios and architectures. Please read the [Overview](https://docs.spring.io/spring/docs/current/spring-framework-reference/overview.html#spring-introduction) section as reference for a more complete introduction. ## Code of Conduct -This project is governed by the [Spring Code of Conduct](CODE_OF_CONDUCT.adoc). -By participating you are expected to uphold this code. -Please report unacceptable behavior to spring-code-of-conduct@pivotal.io. +This project is governed by the [Spring Code of Conduct](CODE_OF_CONDUCT.adoc). By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io. ## Access to Binaries -For access to artifacts or a distribution zip, see the -[Spring Framework Artifacts](https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Artifacts) -wiki page. +For access to artifacts or a distribution zip, see the [Spring Framework Artifacts](https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Artifacts) wiki page. ## Documentation -The Spring Frameworks maintains reference documentation -([published](http://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](http://docs.spring.io/spring-framework/docs/current/javadoc-api/). -There are also [guides and tutorials](https://spring.io/guides) across Spring projects. +The Spring Framework maintains reference documentation ([published](http://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](http://docs.spring.io/spring-framework/docs/current/javadoc-api/). There are also [guides and tutorials](https://spring.io/guides) across Spring projects. ## Build from Source -See the [Build from Source](https://github.com/spring-projects/spring-framework/wiki/Build-from-Source) -wiki page and also [CONTRIBUTING.md](CONTRIBUTING.md). +See the [Build from Source](https://github.com/spring-projects/spring-framework/wiki/Build-from-Source) Wikipedia page and the [CONTRIBUTING.md](CONTRIBUTING.md) file. ## 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](http://spring.io/blog/), -and releases are announced via our [news feed](http://spring.io/blog/category/news). +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](http://spring.io/blog/), and releases are announced via our [news feed](http://spring.io/blog/category/news). ## License -The Spring Framework is released under version 2.0 of the -[Apache License](http://www.apache.org/licenses/LICENSE-2.0). +The Spring Framework is released under version 2.0 of the [Apache License](http://www.apache.org/licenses/LICENSE-2.0). diff --git a/build.gradle b/build.gradle index 471732bea1fc..010d0aa8f93c 100644 --- a/build.gradle +++ b/build.gradle @@ -3,37 +3,55 @@ buildscript { maven { url "https://repo.spring.io/plugins-release" } } dependencies { - classpath("io.spring.gradle:propdeps-plugin:0.0.8") - classpath("io.spring.gradle:docbook-reference-plugin:0.3.1") + classpath("io.spring.gradle:propdeps-plugin:0.0.9.RELEASE") classpath("org.asciidoctor:asciidoctorj-pdf:1.5.0-alpha.16") - classpath("org.asciidoctor:asciidoctorj-epub3:1.5.0-alpha.7") } } // 3rd party plugin repositories can be configured in settings.gradle plugins { - id "com.gradle.build-scan" version "1.8" - id "io.spring.dependency-management" version "1.0.3.RELEASE" apply false - id "org.jetbrains.kotlin.jvm" version "1.2.41" apply false + id "io.spring.dependency-management" version "1.0.5.RELEASE" apply false + id "org.jetbrains.kotlin.jvm" version "1.3.21" apply false id "org.jetbrains.dokka" version "0.9.17" - id "org.asciidoctor.convert" version "1.5.6" -} - -buildScan { - licenseAgreementUrl = 'https://gradle.com/terms-of-service' - licenseAgree = 'yes' + id "org.asciidoctor.convert" version "1.5.8" } ext { - linkHomepage = 'https://projects.spring.io/spring-framework' - linkCi = 'https://build.spring.io/browse/SPR' - linkIssue = 'https://jira.spring.io/browse/SPR' - linkScmUrl = 'https://github.com/spring-projects/spring-framework' - linkScmConnection = 'scm:git:git://github.com/spring-projects/spring-framework.git' - linkScmDevConnection = 'scm:git:ssh://git@github.com:spring-projects/spring-framework.git' + linkHomepage = "https://projects.spring.io/spring-framework" + linkCi = "https://build.spring.io/browse/SPR" + linkIssue = "https://github.com/spring-projects/spring-framework/issues" + linkScmUrl = "https://github.com/spring-projects/spring-framework" + linkScmConnection = "scm:git:git://github.com/spring-projects/spring-framework.git" + linkScmDevConnection = "scm:git:ssh://git@github.com:spring-projects/spring-framework.git" moduleProjects = subprojects.findAll { - !it.name.equals('spring-build-src') && !it.name.equals('spring-framework-bom') + !it.name.equals("spring-build-src") && !it.name.equals("spring-framework-bom") + } + + aspectjVersion = "1.9.2" + coroutinesVersion = "1.1.1" + freemarkerVersion = "2.3.28" + groovyVersion = "2.5.6" + hsqldbVersion = "2.4.1" + jackson2Version = "2.9.8" + jettyVersion = "9.4.15.v20190215" + junit5Version = "5.4.0" + kotlinVersion = "1.3.21" + log4jVersion = "2.11.2" + nettyVersion = "4.1.34.Final" + quartzVersion = "2.3.0" + reactorVersion = "Californium-SR5" + rxjavaVersion = "1.3.8" + rxjavaAdapterVersion = "1.2.1" + rxjava2Version = "2.2.7" + slf4jVersion = "1.7.26" // spring-jcl + consistent 3rd party deps + tiles3Version = "3.0.8" + tomcatVersion = "9.0.16" + undertowVersion = "2.0.19.Final" + + gradleScriptDir = "${rootProject.projectDir}/gradle" + withoutJclOverSlf4J = { + exclude group: "org.slf4j", module: "jcl-over-slf4j" } } @@ -41,62 +59,34 @@ configure(allprojects) { project -> group = "org.springframework" version = qualifyVersionIfNecessary(version) - ext.aspectjVersion = "1.9.1" - ext.freemarkerVersion = "2.3.28" - ext.groovyVersion = "2.5.0" - ext.hsqldbVersion = "2.4.0" - ext.jackson2Version = "2.9.5" - ext.jettyVersion = "9.4.10.v20180503" - ext.junitPlatformVersion = "1.2.0" - ext.junitJupiterVersion = "5.2.0" - ext.junitVintageVersion = "5.2.0" - ext.kotlinVersion = "1.2.41" - ext.log4jVersion = "2.11.0" - ext.nettyVersion = "4.1.25.Final" - ext.reactorVersion = "Californium-BUILD-SNAPSHOT" - ext.rxjavaVersion = "1.3.8" - ext.rxjavaAdapterVersion = "1.2.1" - ext.rxjava2Version = "2.1.14" - ext.slf4jVersion = "1.7.25" // spring-jcl + consistent 3rd party deps - ext.tiles3Version = "3.0.8" - ext.tomcatVersion = "9.0.8" - ext.undertowVersion = "2.0.9.Final" - - ext.gradleScriptDir = "${rootProject.projectDir}/gradle" - - apply plugin: "propdeps" apply plugin: "java" + apply plugin: "kotlin" + apply plugin: "checkstyle" + apply plugin: "propdeps" apply plugin: "test-source-set-dependencies" + apply plugin: "io.spring.dependency-management" apply from: "${gradleScriptDir}/ide.gradle" - apply plugin: "kotlin" - compileKotlin { - kotlinOptions { - jvmTarget = "1.8" - freeCompilerArgs = ["-Xjsr305=strict"] - apiVersion = "1.1" - languageVersion = "1.1" + dependencyManagement { + resolutionStrategy { + cacheChangingModulesFor 0, "seconds" } - } - compileTestKotlin { - kotlinOptions { - jvmTarget = "1.8" - freeCompilerArgs = ["-Xjsr305=strict"] + applyMavenExclusions = false + generatedPomCustomization { + enabled = false } } configurations.all { // Check for updates every build - resolutionStrategy.cacheChangingModulesFor 0, 'seconds' + resolutionStrategy.cacheChangingModulesFor 0, "seconds" // Consistent slf4j version (e.g. clashes between slf4j versions) resolutionStrategy.eachDependency { DependencyResolveDetails details -> - if (details.requested.group == 'org.slf4j') { + if (details.requested.group == "org.slf4j") { details.useVersion slf4jVersion } } - - exclude group: "org.slf4j", module: "jcl-over-slf4j" } def commonCompilerArgs = @@ -109,25 +99,40 @@ configure(allprojects) { project -> "-Xlint:deprecation", "-Xlint:unchecked", "-Werror"] compileTestJava.options*.compilerArgs = commonCompilerArgs + - ["-Xlint:-varargs", "-Xlint:-fallthrough","-Xlint:-rawtypes", + ["-Xlint:-varargs", "-Xlint:-fallthrough", "-Xlint:-rawtypes", "-Xlint:-deprecation", "-Xlint:-unchecked"] compileJava { - sourceCompatibility = 1.8 // can be switched to 10 for testing + sourceCompatibility = 1.8 // can be switched to 11 for testing targetCompatibility = 1.8 - options.encoding = 'UTF-8' + options.encoding = "UTF-8" } compileTestJava { - sourceCompatibility = 1.8 // can be switched to 10 for testing + sourceCompatibility = 1.8 // can be switched to 11 for testing targetCompatibility = 1.8 - options.encoding = 'UTF-8' + options.encoding = "UTF-8" options.compilerArgs += "-parameters" } + compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + freeCompilerArgs = ["-Xjsr305=strict"] + } + } + + compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + freeCompilerArgs = ["-Xjsr305=strict"] + } + } + test { systemProperty("java.awt.headless", "true") systemProperty("testGroups", project.properties.get("testGroups")) + systemProperty("io.netty.leakDetection.level", "paranoid") scanForTestClasses = false include(["**/*Tests.class", "**/*Test.class"]) // Since we set scanForTestClasses to false, we need to filter out inner @@ -137,49 +142,54 @@ configure(allprojects) { project -> reports.junitXml.setDestination(file("$buildDir/test-results")) } + checkstyle { + toolVersion = "8.18" + configDir = rootProject.file("src/checkstyle") + } + repositories { maven { url "https://repo.spring.io/libs-release" } - maven { url "https://repo.spring.io/snapshot" } // for Reactor + maven { url "https://repo.spring.io/snapshot" } // Reactor + mavenLocal() } dependencies { testCompile("junit:junit:4.12") { - exclude group:'org.hamcrest', module:'hamcrest-core' - } - testCompile("org.mockito:mockito-core:2.18.0") { - exclude group:'org.hamcrest', module:'hamcrest-core' + exclude group: "org.hamcrest", module: "hamcrest-core" } - testCompile("com.nhaarman:mockito-kotlin:1.5.0") { - exclude module:'kotlin-stdlib' - exclude module:'kotlin-reflect' - exclude module:'mockito-core' + testCompile("org.mockito:mockito-core:2.25.0") { + exclude group: "org.hamcrest", module: "hamcrest-core" } + testCompile("io.mockk:mockk:1.9.1") testCompile("org.hamcrest:hamcrest-all:1.3") testRuntime("org.apache.logging.log4j:log4j-core:${log4jVersion}") testRuntime("org.apache.logging.log4j:log4j-slf4j-impl:${log4jVersion}") testRuntime("org.apache.logging.log4j:log4j-jul:${log4jVersion}") - // JSR-305 only used for non-required meta-annotations + // JSR-305 only used for non-required meta-annotations compileOnly("com.google.code.findbugs:jsr305:3.0.2") testCompileOnly("com.google.code.findbugs:jsr305:3.0.2") + checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.5") } ext.javadocLinks = [ - "http://docs.oracle.com/javase/8/docs/api/", - "http://docs.oracle.com/javaee/7/api/", - "http://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ - "http://pic.dhe.ibm.com/infocenter/wasinfo/v7r0/topic/com.ibm.websphere.javadoc.doc/web/apidocs/", - "http://glassfish.java.net/nonav/docs/v3/api/", - "http://docs.jboss.org/jbossas/javadoc/4.0.5/connector/", - "http://docs.jboss.org/jbossas/javadoc/7.1.2.Final/", - "http://tiles.apache.org/tiles-request/apidocs/", - "http://tiles.apache.org/framework/apidocs/", - "http://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/", - "http://ehcache.org/apidocs/2.10.4", - "http://quartz-scheduler.org/api/2.2.1/", - "http://fasterxml.github.io/jackson-core/javadoc/2.8/", - "http://fasterxml.github.io/jackson-databind/javadoc/2.8/", - "http://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.8/", - "http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/" + "https://docs.oracle.com/javase/8/docs/api/", + "https://docs.oracle.com/javaee/7/api/", + "https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ + "https://www.ibm.com/support/knowledgecenter/SS7JFU_8.5.5/com.ibm.websphere.javadoc.doc/web/apidocs/", + "https://glassfish.java.net/nonav/docs/v3/api/", + "https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/", + "https://docs.jboss.org/jbossas/javadoc/7.1.2.Final/", + "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.quartz-scheduler.org/api/${quartzVersion}/", + "https://fasterxml.github.io/jackson-core/javadoc/2.9/", + "https://fasterxml.github.io/jackson-databind/javadoc/2.9/", + "https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.9/", + "https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/", + "https://junit.org/junit4/javadoc/4.12/", + "https://junit.org/junit5/docs/${junit5Version}/api/" ] as String[] } @@ -209,7 +219,7 @@ configure(subprojects - project(":spring-build-src")) { subproject -> options.header = project.name options.use = true options.links(project.ext.javadocLinks) - options.addStringOption('Xdoclint:none', '-quiet') + options.addStringOption("Xdoclint:none", "-quiet") // Suppress warnings due to cross-module @see and @link references. // Note that global 'api' task does display all warnings. @@ -219,7 +229,7 @@ configure(subprojects - project(":spring-build-src")) { subproject -> task sourcesJar(type: Jar, dependsOn: classes) { duplicatesStrategy = DuplicatesStrategy.EXCLUDE - classifier = 'sources' + classifier = "sources" from sourceSets.main.allSource // Don't include or exclude anything explicitly by default. See SPR-12085. } @@ -239,7 +249,6 @@ configure(rootProject) { description = "Spring Framework" apply plugin: "groovy" - apply plugin: "io.spring.dependency-management" apply from: "${gradleScriptDir}/jdiff.gradle" apply from: "${gradleScriptDir}/docs.gradle" @@ -247,16 +256,9 @@ configure(rootProject) { imports { mavenBom "io.projectreactor:reactor-bom:${reactorVersion}" } - dependencies { - dependency "io.projectreactor.netty:reactor-netty:0.8.0.BUILD-SNAPSHOT" - } - resolutionStrategy { - cacheChangingModulesFor 0, 'seconds' - } - applyMavenExclusions = false } - // don't publish the default jar for the root project + // Don't publish the default jar for the root project configurations.archives.artifacts.clear() dependencies { // for integration tests @@ -271,11 +273,11 @@ configure(rootProject) { testCompile(project(":spring-tx")) testCompile(project(":spring-web")) testCompile("javax.inject:javax.inject:1") - testCompile("javax.resource:javax.resource-api:1.7") + testCompile("javax.resource:javax.resource-api:1.7.1") testCompile("javax.servlet:javax.servlet-api:3.1.0") testCompile("org.aspectj:aspectjweaver:${aspectjVersion}") testCompile("org.hsqldb:hsqldb:${hsqldbVersion}") - testCompile("org.hibernate:hibernate-core:5.1.14.Final") + testCompile("org.hibernate:hibernate-core:5.1.17.Final") } artifacts { @@ -285,9 +287,6 @@ configure(rootProject) { } wrapper { - description = "Generates gradlew[.bat] scripts" - gradleVersion = '4.8' - doLast() { def gradleOpts = "-XX:MaxMetaspaceSize=1024m -Xmx1024m" def gradleBatOpts = "$gradleOpts -XX:MaxHeapSize=256m" @@ -299,7 +298,6 @@ configure(rootProject) { "set GRADLE_OPTS=$gradleBatOpts %GRADLE_OPTS%\nset DEFAULT_JVM_OPTS=") } } - } /* @@ -313,7 +311,7 @@ def qualifyVersionIfNecessary(version) { if (rootProject.hasProperty("BRANCH_NAME")) { def qualifier = rootProject.getProperty("BRANCH_NAME") if (qualifier.startsWith("SPR-")) { - return version.replace('BUILD', qualifier) + return version.replace("BUILD", qualifier) } } return version diff --git a/gradle.properties b/gradle.properties index 6adf0ecbeb98..c91267f0638a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=5.1.0.BUILD-SNAPSHOT +version=5.2.0.BUILD-SNAPSHOT diff --git a/gradle/docs.gradle b/gradle/docs.gradle index 2bf02b567508..af005bef800c 100644 --- a/gradle/docs.gradle +++ b/gradle/docs.gradle @@ -1,11 +1,11 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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 * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -27,6 +27,7 @@ task api(type: Javadoc) { options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED options.author = true options.header = rootProject.description + options.use = true options.overview = "src/docs/api/overview.html" options.stylesheetFile = file("src/docs/api/stylesheet.css") options.splitIndex = true @@ -71,20 +72,39 @@ dokka { packageListUrl = new File(buildDir, "api/package-list").toURI().toURL() } externalDocumentationLink { - url = new URL("http://projectreactor.io/docs/core/release/api/") + url = new URL("https://projectreactor.io/docs/core/release/api/") } externalDocumentationLink { - url = new URL("http://www.reactive-streams.org/reactive-streams-1.0.1-javadoc/") + url = new URL("https://www.reactive-streams.org/reactive-streams-1.0.1-javadoc/") } } +configurations { + docs +} + +dependencies { + // for documentation resources + docs "io.spring.docresources:spring-doc-resources:0.1.0.RELEASE@zip" +} + +task extractDocResources(type: Sync) { + dependsOn configurations.docs + from "src/docs/asciidoc/" + from { + configurations.docs.collect { zipTree(it) } + } + into "$buildDir/asciidoc/build" +} + asciidoctor { + sourceDir "$buildDir/asciidoc/build" sources { include '*.adoc' } resources { from(sourceDir) { - include 'images/*', 'stylesheets/*', 'tocbot-3.0.2/*' + include 'images/*', 'css/**', 'js/**' } } logDocuments = true @@ -97,23 +117,27 @@ asciidoctor { attributes 'icons': 'font', 'idprefix': '', 'idseparator': '-', - docinfo: '', + docinfo: 'shared', revnumber: project.version, sectanchors: '', sectnums: '', - 'source-highlighter': 'coderay@', // TODO switch to 'rouge' once supported by the html5 backend - stylesdir: 'stylesheets/', - stylesheet: 'main.css', + 'source-highlighter=highlight.js', + 'highlightjsdir=js/highlight', + 'highlightjs-theme=atom-one-dark-reasonable', + stylesdir: "css/", + stylesheet: 'spring.css', 'spring-version': project.version } +asciidoctor.dependsOn extractDocResources + task docsZip(type: Zip, dependsOn: ['api', 'asciidoctor', 'dokka']) { group = "Distribution" baseName = "spring-framework" classifier = "docs" description = "Builds -${classifier} archive containing api and reference " + - "for deployment at http://docs.spring.io/spring-framework/docs." + "for deployment at https://docs.spring.io/spring-framework/docs." from("src/dist") { include "changelog.txt" @@ -141,7 +165,7 @@ task schemaZip(type: Zip) { baseName = "spring-framework" classifier = "schema" description = "Builds -${classifier} archive containing all " + - "XSDs for deployment at http://springframework.org/schema." + "XSDs for deployment at https://springframework.org/schema." duplicatesStrategy 'exclude' moduleProjects.each { subproject -> def Properties schemas = new Properties(); diff --git a/gradle/ide.gradle b/gradle/ide.gradle index 469733d7a509..85fe8b4cf7f0 100644 --- a/gradle/ide.gradle +++ b/gradle/ide.gradle @@ -11,7 +11,7 @@ eclipse.jdt { } // Replace classpath entries with project dependencies (GRADLE-1116) -// http://issues.gradle.org/browse/GRADLE-1116 +// https://issues.gradle.org/browse/GRADLE-1116 eclipse.classpath.file.whenMerged { classpath -> def regexp = /.*?\/([^\/]+)\/build\/([^\/]+\/)+(?:main|test)/ // only match those that end in main or test (avoids removing necessary entries like build/classes/jaxb) def projectOutputDependencies = classpath.entries.findAll { entry -> entry.path =~ regexp } @@ -91,6 +91,14 @@ task cleanEclipseJdtUi(type: Delete) { delete project.file(".settings/org.eclipse.wst.common.project.facet.core.xml") } +task eclipseBuildship(type: Copy) { + from rootProject.files( + "src/eclipse/org.eclipse.jdt.ui.prefs", + "src/eclipse/org.eclipse.jdt.core.prefs") + into project.file('.settings/') + outputs.upToDateWhen { false } +} + tasks["eclipseJdt"].dependsOn(eclipseJdtPrepare) tasks["cleanEclipse"].dependsOn(cleanEclipseJdtUi) tasks["eclipse"].dependsOn(eclipseSettings, eclipseWstComponent) diff --git a/gradle/publish-maven.gradle b/gradle/publish-maven.gradle index eff54387f693..dad95c8452fc 100644 --- a/gradle/publish-maven.gradle +++ b/gradle/publish-maven.gradle @@ -18,6 +18,11 @@ def customizePom(pom, gradleProject) { "$dep.scope:$dep.groupId:$dep.artifactId" } + def managedVersions = dependencyManagement.managedVersions + generatedPom.dependencies.findAll{dep -> !dep.version }.each { dep -> + dep.version = managedVersions["${dep.groupId}:${dep.artifactId}"] + } + // add all items necessary for maven central publication generatedPom.project { name = gradleProject.description @@ -25,12 +30,12 @@ def customizePom(pom, gradleProject) { url = "https://github.com/spring-projects/spring-framework" organization { name = "Spring IO" - url = "http://projects.spring.io/spring-framework" + url = "https://projects.spring.io/spring-framework" } licenses { license { name "Apache License, Version 2.0" - url "http://www.apache.org/licenses/LICENSE-2.0" + url "https://www.apache.org/licenses/LICENSE-2.0" distribution "repo" } } @@ -47,8 +52,8 @@ def customizePom(pom, gradleProject) { } } issueManagement { - system = "Jira" - url = "https://jira.springsource.org/browse/SPR" + system = "GitHub" + url = "https://github.com/spring-projects/spring-framework/issues" } } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1948b9074f10..94336fcae912 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 d2c45a4b2603..290541c73864 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-4.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/import-into-eclipse.bat b/import-into-eclipse.bat deleted file mode 100644 index b6532a169d89..000000000000 --- a/import-into-eclipse.bat +++ /dev/null @@ -1,122 +0,0 @@ -@echo off - -set CURRENT_DIR=%~dp0 -cd %CURRENT_DIR% - -cls - -echo. -echo ----------------------------------------------------------------------- -echo Spring Framework - Eclipse/STS project import guide -echo. -echo This script will guide you through the process of importing the Spring -echo Framework projects into Eclipse or the Spring Tool Suite (STS). It is -echo recommended that you have a recent version of Eclipse or STS. As a bare -echo minimum you will need Eclipse with full Java 8 support, the AspectJ -echo Development Tools (AJDT), and the Groovy Compiler. -echo. -echo If you need to download and install Eclipse or STS, please do that now -echo by visiting one of the following sites: -echo. -echo - Eclipse downloads: http://download.eclipse.org/eclipse/downloads -echo - STS downloads: http://spring.io/tools/sts/all -echo - STS nightly builds: http://dist.springsource.com/snapshot/STS/nightly-distributions.html -echo - ADJT: http://www.eclipse.org/ajdt/downloads/ -echo - Groovy Eclipse: https://github.com/groovy/groovy-eclipse/wiki -echo. -echo Otherwise, press enter and we'll begin. - -pause - -REM this command: -REM - wipes out any existing Eclipse metadata -REM - generates OXM test classes to avoid errors on import into Eclipse -REM - generates metadata for all subprojects -REM - skips metadata gen for the root project (-x :eclipse) to work -REM around Eclipse's inability to import hierarchical project structures -REM SET COMMAND="./gradlew --no-daemon cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse" -SET COMMAND=gradlew --no-daemon cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse - -echo. -echo ----------------------------------------------------------------------- -echo STEP 1: Generate subproject Eclipse metadata -echo. -echo The first step will be to generate Eclipse project metadata for each -echo of the spring-* subprojects. This happens via the built-in -echo "Gradle wrapper" script (./gradlew in this directory). If this is your -echo first time using the Gradle wrapper, this step may take a few minutes -echo while a Gradle distribution is downloaded for you. -echo. -echo The command run will be: -echo. -echo %COMMAND% -echo. -echo Press enter when ready. - -pause - -call %COMMAND% -if not "%ERRORLEVEL%" == "0" exit /B %ERRORLEVEL% - -echo. -echo ----------------------------------------------------------------------- -echo STEP 2: Import subprojects into Eclipse/STS -echo. -echo Within Eclipse/STS, do the following: -echo. -echo File ^> Import... ^> Existing Projects into Workspace -echo ^> When prompted for the 'root directory', provide %CURRENT_DIR% -echo ^> Press enter. You will see the modules show up under "Projects" -echo ^> All projects should be selected/checked. Click Finish. -echo ^> When the project import is complete, you should have no errors. -echo. -echo When the above is complete, return here and press the enter key. - -pause - -set COMMAND=gradlew --no-daemon :eclipse - -echo. -echo ----------------------------------------------------------------------- -echo STEP 3: generate root project Eclipse metadata -echo. -echo Unfortunately, Eclipse does not allow for importing project -echo hierarchies, so we had to skip root project metadata generation in the -echo during step 1. In this step we simply generate root project metadata -echo so you can import it in the next step. -echo. -echo The command run will be: -echo. -echo %COMMAND% -echo. -echo Press the enter key when ready. -pause - -call %COMMAND% -if not "%ERRORLEVEL%" == "0" exit /B %ERRORLEVEL% - -echo. -echo ----------------------------------------------------------------------- -echo STEP 4: Import root project into Eclipse/STS -echo. -echo Follow the project import steps listed in step 2 above to import the -echo root project. -echo. -echo Press enter when complete, and move on to the final step. - -pause - -echo. -echo ----------------------------------------------------------------------- -echo STEP 5: Enable Git support for all projects -echo. -echo - In the Eclipse/STS Package Explorer, select all spring* projects. -echo - Right-click to open the context menu and select Team ^> Share Project... -echo - In the Share Project dialog that appears, select Git and press Next -echo - Check "Use or create repository in parent folder of project" -echo - Click Finish -echo. -echo When complete, you'll have Git support enabled for all projects. -echo. -echo You're ready to code! Goodbye! - diff --git a/import-into-eclipse.md b/import-into-eclipse.md new file mode 100644 index 000000000000..adc6cd3dc71b --- /dev/null +++ b/import-into-eclipse.md @@ -0,0 +1,49 @@ +# Spring Framework - Eclipse/STS Project Import Guide + +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 or STS. As a bare minimum you will need Eclipse with full Java +8 support, the AspectJ Development Tools (AJDT), and the Groovy Compiler. + +The following instructions have been tested against +[Spring Tool Suite](https://spring.io/tools) (_STS_) 3.9.4 and 4.0.0.M11 with +[Eclipse Buildship](http://projects.eclipse.org/projects/tools.buildship) (Eclipse +Plug-ins for Gradle). 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. + +## Steps + +_Within your locally cloned `spring-framework` working directory:_ + +1. Precompile `spring-oxm` with `./gradlew :spring-oxm:compileTestJava` +2. Import into Eclipse (File -> Import -> Gradle -> Existing Gradle Project -> Navigate + to directory -> Select Finish) +3. If prompted, exclude the `spring-aspects` module (or after the import by closing or + deleting the project) +4. In the `spring-oxm` project, add the `jaxb` folder in + `build/generated-sources` to the build path (right click on them and select + `Build Path -> Use as Source Folder`) +5. To apply project specific settings run `./gradlew eclipseBuildship` +7. Code away + +## Known Issues + +1. `spring-core` and `spring-oxm` should be pre-compiled due to repackaged dependencies. + - See `*RepackJar` tasks in the build. +2. `spring-aspects` does not compile due to references to aspect types unknown to Eclipse. + - If you install [AJDT](https://www.eclipse.org/ajdt/downloads/) into Eclipse it should + work. +3. While JUnit tests pass from the command line with Gradle, some may fail when run from + the IDE. + - Resolving this is a work in progress. + - If attempting to run all JUnit tests from within the IDE, you will likely need to set + the following VM options to avoid out of memory errors: + `-XX:MaxPermSize=2048m -Xmx2048m -XX:MaxHeapSize=2048m` + +## Tips + +In any case, please do not check in your own generated `.classpath` file, `.project` +file, or `.settings` folder. You'll notice these files are already intentionally in +`.gitignore`. The same policy holds for IDEA metadata. diff --git a/import-into-eclipse.sh b/import-into-eclipse.sh deleted file mode 100755 index b513f0754f38..000000000000 --- a/import-into-eclipse.sh +++ /dev/null @@ -1,129 +0,0 @@ -cd `dirname $0` -clear -cat < Import... > Existing Projects into Workspace -- When prompted for the 'root directory', provide $PWD. -- Press enter. You will see the modules show up under "Projects". -- All projects should be selected/checked. Click Finish. -- When the project import is complete, you should have no errors. - -When the above is complete, return here and press the enter key. -EOM - -read - -COMMAND="./gradlew --no-daemon :eclipse" - -cat < Share Project... -- In the Share Project dialog that appears, select Git and press Next. -- Check "Use or create repository in parent folder of project". -- Click Finish. - -When complete, you'll have Git support enabled for all projects. - -You're ready to code! Goodbye! -EOM diff --git a/settings.gradle b/settings.gradle index 3f17c52cf9d9..34f40dc429d8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -25,7 +25,7 @@ include "spring-framework-bom" include "buildSrc" rootProject.children.find{ it.name == "buildSrc" }.name = "spring-build-src" -rootProject.name = 'spring' +rootProject.name = "spring" rootProject.children.each {project -> project.buildFileName = "${project.name}.gradle" } diff --git a/spring-aop/spring-aop.gradle b/spring-aop/spring-aop.gradle index 12573e5a4389..6911a3b5f203 100644 --- a/spring-aop/spring-aop.gradle +++ b/spring-aop/spring-aop.gradle @@ -2,8 +2,8 @@ description = "Spring AOP" dependencies { compile(project(":spring-beans")) - compile(project(':spring-core')) + compile(project(":spring-core")) optional("org.aspectj:aspectjweaver:${aspectjVersion}") - optional("org.apache.commons:commons-pool2:2.5.0") + optional("org.apache.commons:commons-pool2:2.6.0") optional("com.jamonapi:jamon:2.81") } diff --git a/spring-aop/src/main/java/org/aopalliance/aop/AspectException.java b/spring-aop/src/main/java/org/aopalliance/aop/AspectException.java index c634d51a06a5..3b915e6332da 100644 --- a/spring-aop/src/main/java/org/aopalliance/aop/AspectException.java +++ b/spring-aop/src/main/java/org/aopalliance/aop/AspectException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author 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 @@ * Superclass for all AOP infrastructure exceptions. * Unchecked, as such exceptions are fatal and end user * code shouldn't be forced to catch them. - * + * * @author Rod Johnson * @author Bob Lee * @author Juergen Hoeller 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 a453ac32e36d..1adb71fb8a9f 100644 --- a/spring-aop/src/main/java/org/aopalliance/intercept/ConstructorInvocation.java +++ b/spring-aop/src/main/java/org/aopalliance/intercept/ConstructorInvocation.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author 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 java.lang.reflect.Constructor; /** - * Description of an invocation to a constuctor, given to an + * Description of an invocation to a constructor, given to an * interceptor upon constructor-call. * *

A constructor invocation is a joinpoint and can be intercepted @@ -30,12 +30,12 @@ */ public interface ConstructorInvocation extends Invocation { - /** - * Get the constructor being called. - *

This method is a friendly implementation of the - * {@link Joinpoint#getStaticPart()} method (same result). - * @return the constructor being called - */ - Constructor getConstructor(); + /** + * Get the constructor being called. + *

This method is a friendly implementation of the + * {@link Joinpoint#getStaticPart()} method (same result). + * @return the constructor being called + */ + Constructor getConstructor(); } diff --git a/spring-aop/src/main/java/org/aopalliance/intercept/Interceptor.java b/spring-aop/src/main/java/org/aopalliance/intercept/Interceptor.java index eef409a74b53..f9742547291e 100644 --- a/spring-aop/src/main/java/org/aopalliance/intercept/Interceptor.java +++ b/spring-aop/src/main/java/org/aopalliance/intercept/Interceptor.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. @@ -24,7 +24,7 @@ *

A generic interceptor can intercept runtime events that occur * within a base program. Those events are materialized by (reified * in) joinpoints. Runtime joinpoints can be invocations, field - * access, exceptions... + * access, exceptions... * *

This interface is not used directly. Use the sub-interfaces * to intercept specific events. For instance, the following class @@ -32,8 +32,8 @@ * debugger: * *

- * class DebuggingInterceptor implements MethodInterceptor, 
- *     ConstructorInterceptor, FieldInterceptor {
+ * class DebuggingInterceptor implements MethodInterceptor,
+ *     ConstructorInterceptor {
  *
  *   Object invoke(MethodInvocation i) throws Throwable {
  *     debug(i.getMethod(), i.getThis(), i.getArgs());
@@ -44,16 +44,6 @@
  *     debug(i.getConstructor(), i.getThis(), i.getArgs());
  *     return i.proceed();
  *   }
- * 
- *   Object get(FieldAccess fa) throws Throwable {
- *     debug(fa.getField(), fa.getThis(), null);
- *     return fa.proceed();
- *   }
- *
- *   Object set(FieldAccess fa) throws Throwable {
- *     debug(fa.getField(), fa.getThis(), fa.getValueToSet());
- *     return fa.proceed();
- *   }
  *
  *   void debug(AccessibleObject ao, Object this, Object value) {
  *     ...
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 8239b0e63b7e..006517d34260 100644
--- a/spring-aop/src/main/java/org/aopalliance/intercept/MethodInterceptor.java
+++ b/spring-aop/src/main/java/org/aopalliance/intercept/MethodInterceptor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 the original author 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 @@
  */
 @FunctionalInterface
 public interface MethodInterceptor extends Interceptor {
-	
+
 	/**
 	 * Implement this method to perform extra treatments before and
 	 * after the invocation. Polite implementations would certainly
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 6314824d7659..cb467ff88506 100644
--- a/spring-aop/src/main/java/org/aopalliance/intercept/MethodInvocation.java
+++ b/spring-aop/src/main/java/org/aopalliance/intercept/MethodInvocation.java
@@ -32,7 +32,7 @@ public interface MethodInvocation extends Invocation {
 
 	/**
 	 * Get the method being called.
-	 * 

This method is a frienly implementation of the + *

This method is a friendly implementation of the * {@link Joinpoint#getStaticPart()} method (same result). * @return the method being called */ diff --git a/spring-aop/src/main/java/org/springframework/aop/IntroductionAwareMethodMatcher.java b/spring-aop/src/main/java/org/springframework/aop/IntroductionAwareMethodMatcher.java index eceac03ea29a..a5ea6e88ce0f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/IntroductionAwareMethodMatcher.java +++ b/spring-aop/src/main/java/org/springframework/aop/IntroductionAwareMethodMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,6 @@ import java.lang.reflect.Method; -import org.springframework.lang.Nullable; - /** * A specialized type of {@link MethodMatcher} that takes into account introductions * when matching methods. If there are no introductions on the target class, @@ -35,12 +33,11 @@ public interface IntroductionAwareMethodMatcher extends MethodMatcher { * instead of the 2-arg {@link #matches(java.lang.reflect.Method, Class)} method * if the caller supports the extended IntroductionAwareMethodMatcher interface. * @param method the candidate method - * @param targetClass the target class (may be {@code null}, in which case - * the candidate class must be taken to be the method's declaring class) + * @param targetClass the target class * @param hasIntroductions {@code true} if the object on whose behalf we are * asking is the subject on one or more introductions; {@code false} otherwise * @return whether or not this method matches statically */ - boolean matches(Method method, @Nullable Class targetClass, boolean hasIntroductions); + boolean matches(Method method, Class targetClass, boolean hasIntroductions); } 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 cb6f8da209af..a390228202ea 100644 --- a/spring-aop/src/main/java/org/springframework/aop/MethodBeforeAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/MethodBeforeAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author 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,10 +24,9 @@ * Advice invoked before a method is invoked. Such advices cannot * prevent the method call proceeding, unless they throw a Throwable. * + * @author Rod Johnson * @see AfterReturningAdvice * @see ThrowsAdvice - * - * @author Rod Johnson */ public interface MethodBeforeAdvice extends BeforeAdvice { diff --git a/spring-aop/src/main/java/org/springframework/aop/MethodMatcher.java b/spring-aop/src/main/java/org/springframework/aop/MethodMatcher.java index 7b863b071236..9a31e33fda92 100644 --- a/spring-aop/src/main/java/org/springframework/aop/MethodMatcher.java +++ b/spring-aop/src/main/java/org/springframework/aop/MethodMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,6 @@ import java.lang.reflect.Method; -import org.springframework.lang.Nullable; - /** * Part of a {@link Pointcut}: Checks whether the target method is eligible for advice. * @@ -50,16 +48,16 @@ public interface MethodMatcher { /** - * Perform static checking whether the given method matches. If this - * returns {@code false} or if the {@link #isRuntime()} method - * returns {@code false}, no runtime check (i.e. no. - * {@link #matches(java.lang.reflect.Method, Class, Object[])} call) will be made. + * Perform static checking whether the given method matches. + *

If this returns {@code false} or if the {@link #isRuntime()} + * method returns {@code false}, no runtime check (i.e. no + * {@link #matches(java.lang.reflect.Method, Class, Object[])} call) + * will be made. * @param method the candidate method - * @param targetClass the target class (may be {@code null}, in which case - * the candidate class must be taken to be the method's declaring class) + * @param targetClass the target class * @return whether or not this method matches statically */ - boolean matches(Method method, @Nullable Class targetClass); + boolean matches(Method method, Class targetClass); /** * Is this MethodMatcher dynamic, that is, must a final call be made on the @@ -82,13 +80,12 @@ public interface MethodMatcher { * immediately before potential running of the advice, after any * advice earlier in the advice chain has run. * @param method the candidate method - * @param targetClass the target class (may be {@code null}, in which case - * the candidate class must be taken to be the method's declaring class) + * @param targetClass the target class * @param args arguments to the method * @return whether there's a runtime match * @see MethodMatcher#matches(Method, Class) */ - boolean matches(Method method, @Nullable Class targetClass, Object... args); + boolean matches(Method method, Class targetClass, Object... args); /** diff --git a/spring-aop/src/main/java/org/springframework/aop/TargetSource.java b/spring-aop/src/main/java/org/springframework/aop/TargetSource.java index 61afc9d4bbad..07d42e84c99d 100644 --- a/spring-aop/src/main/java/org/springframework/aop/TargetSource.java +++ b/spring-aop/src/main/java/org/springframework/aop/TargetSource.java @@ -1,5 +1,5 @@ -/*< - * Copyright 2002-2017 the original author or authors. +/* + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-aop/src/main/java/org/springframework/aop/TrueClassFilter.java b/spring-aop/src/main/java/org/springframework/aop/TrueClassFilter.java index 4777e757ba56..8009b3ccad5c 100644 --- a/spring-aop/src/main/java/org/springframework/aop/TrueClassFilter.java +++ b/spring-aop/src/main/java/org/springframework/aop/TrueClassFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author 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 @@ * @author Rod Johnson */ @SuppressWarnings("serial") -class TrueClassFilter implements ClassFilter, Serializable { +final class TrueClassFilter implements ClassFilter, Serializable { public static final TrueClassFilter INSTANCE = new TrueClassFilter(); diff --git a/spring-aop/src/main/java/org/springframework/aop/TrueMethodMatcher.java b/spring-aop/src/main/java/org/springframework/aop/TrueMethodMatcher.java index 7442d72ec4a2..966ef7951230 100644 --- a/spring-aop/src/main/java/org/springframework/aop/TrueMethodMatcher.java +++ b/spring-aop/src/main/java/org/springframework/aop/TrueMethodMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2018 the original author 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,15 +19,13 @@ import java.io.Serializable; import java.lang.reflect.Method; -import org.springframework.lang.Nullable; - /** * Canonical MethodMatcher instance that matches all methods. * * @author Rod Johnson */ @SuppressWarnings("serial") -class TrueMethodMatcher implements MethodMatcher, Serializable { +final class TrueMethodMatcher implements MethodMatcher, Serializable { public static final TrueMethodMatcher INSTANCE = new TrueMethodMatcher(); @@ -45,12 +43,12 @@ public boolean isRuntime() { } @Override - public boolean matches(Method method, @Nullable Class targetClass) { + public boolean matches(Method method, Class targetClass) { return true; } @Override - public boolean matches(Method method, @Nullable Class targetClass, Object... args) { + public boolean matches(Method method, Class targetClass, Object... args) { // Should never be invoked as isRuntime returns false. throw new UnsupportedOperationException(); } diff --git a/spring-aop/src/main/java/org/springframework/aop/TruePointcut.java b/spring-aop/src/main/java/org/springframework/aop/TruePointcut.java index 1a44c2ac2684..a6c8c9b92d76 100644 --- a/spring-aop/src/main/java/org/springframework/aop/TruePointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/TruePointcut.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author 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 @@ * @author Rod Johnson */ @SuppressWarnings("serial") -class TruePointcut implements Pointcut, Serializable { +final class TruePointcut implements Pointcut, Serializable { public static final TruePointcut INSTANCE = new TruePointcut(); 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 37e9a596a8d6..5c22ed46e1c5 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 @@ -63,7 +63,7 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedenceInformation, Serializable { /** - * Key used in ReflectiveMethodInvocation userAtributes map for the current joinpoint. + * Key used in ReflectiveMethodInvocation userAttributes map for the current joinpoint. */ protected static final String JOIN_POINT_KEY = JoinPoint.class.getName(); @@ -117,16 +117,16 @@ public static JoinPoint currentJoinPoint() { /** * This will be non-null if the creator of this advice object knows the argument names - * and sets them explicitly + * and sets them explicitly. */ @Nullable private String[] argumentNames; - /** Non-null if after throwing advice binds the thrown value */ + /** Non-null if after throwing advice binds the thrown value. */ @Nullable private String throwingName; - /** Non-null if after returning advice binds the return value */ + /** Non-null if after returning advice binds the return value. */ @Nullable private String returningName; @@ -136,13 +136,13 @@ public static JoinPoint currentJoinPoint() { /** * Index for thisJoinPoint argument (currently only - * supported at index 0 if present at all) + * supported at index 0 if present at all). */ private int joinPointArgumentIndex = -1; /** * Index for thisJoinPointStaticPart argument (currently only - * supported at index 0 if present at all) + * supported at index 0 if present at all). */ private int joinPointStaticPartArgumentIndex = -1; @@ -551,7 +551,7 @@ private void configurePointcutParameters(String[] argumentNames, int argumentInd /** * Take the arguments at the method execution join point and output a set of arguments - * to the advice method + * to the advice method. * @param jp the current JoinPoint * @param jpMatch the join point match that matched this execution join point * @param returnValue the return value from the method execution (may be null) @@ -715,7 +715,7 @@ public AdviceExcludingMethodMatcher(Method adviceMethod) { } @Override - public boolean matches(Method method, @Nullable Class targetClass) { + public boolean matches(Method method, Class targetClass) { return !this.adviceMethod.equals(method); } 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 d1a9c25c94ae..44150e6a3d80 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-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -156,17 +156,17 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov } - /** The pointcut expression associated with the advice, as a simple String */ + /** The pointcut expression associated with the advice, as a simple String. */ @Nullable private String pointcutExpression; private boolean raiseExceptions; - /** If the advice is afterReturning, and binds the return value, this is the parameter name used */ + /** If the advice is afterReturning, and binds the return value, this is the parameter name used. */ @Nullable private String returningName; - /** If the advice is afterThrowing, and binds the thrown value, this is the parameter name used */ + /** If the advice is afterThrowing, and binds the thrown value, this is the parameter name used. */ @Nullable private String throwingName; @@ -178,7 +178,7 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov /** - * Create a new discoverer that attempts to discover parameter names + * Create a new discoverer that attempts to discover parameter names. * from the given pointcut expression. */ public AspectJAdviceParameterNameDiscoverer(@Nullable String pointcutExpression) { @@ -272,15 +272,7 @@ public String[] getParameterNames(Method method) { } } } - catch (AmbiguousBindingException ambigEx) { - if (this.raiseExceptions) { - throw ambigEx; - } - else { - return null; - } - } - catch (IllegalArgumentException ex) { + catch (AmbiguousBindingException | IllegalArgumentException ex) { if (this.raiseExceptions) { throw ex; } @@ -677,7 +669,7 @@ private PointcutBody getPointcutBody(String[] tokens, int startIndex) { } /** - * Match up args against unbound arguments of primitive types + * Match up args against unbound arguments of primitive types. */ private void maybeBindPrimitiveArgsFromPointcutExpression() { int numUnboundPrimitives = countNumberOfUnboundPrimitiveArguments(); diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java index 5f3a68e4ca67..ec4f62a2bb97 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java @@ -290,7 +290,7 @@ public boolean matches(Class targetClass) { } @Override - public boolean matches(Method method, @Nullable Class targetClass, boolean hasIntroductions) { + public boolean matches(Method method, Class targetClass, boolean hasIntroductions) { obtainPointcutExpression(); ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass); @@ -313,13 +313,12 @@ else if (shadowMatch.neverMatches()) { // we say this is not a match as in Spring there will never be a different // runtime subtype. RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch); - return (!walker.testsSubtypeSensitiveVars() || - (targetClass != null && walker.testTargetInstanceOfResidue(targetClass))); + return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass)); } } @Override - public boolean matches(Method method, @Nullable Class targetClass) { + public boolean matches(Method method, Class targetClass) { return matches(method, targetClass, false); } @@ -329,7 +328,7 @@ public boolean isRuntime() { } @Override - public boolean matches(Method method, @Nullable Class targetClass, Object... args) { + public boolean matches(Method method, Class targetClass, Object... args) { obtainPointcutExpression(); ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass); @@ -426,17 +425,23 @@ private void bindParameters(ProxyMethodInvocation invocation, JoinPointMatch jpm invocation.setUserAttribute(resolveExpression(), jpm); } - private ShadowMatch getTargetShadowMatch(Method method, @Nullable Class targetClass) { + private ShadowMatch getTargetShadowMatch(Method method, Class targetClass) { Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass); - if (targetClass != null && targetMethod.getDeclaringClass().isInterface()) { + if (targetMethod.getDeclaringClass().isInterface()) { // Try to build the most specific interface possible for inherited methods to be // considered for sub-interface matches as well, in particular for proxy classes. // Note: AspectJ is only going to take Method.getDeclaringClass() into account. Set> ifcs = ClassUtils.getAllInterfacesForClassAsSet(targetClass); if (ifcs.size() > 1) { - Class compositeInterface = ClassUtils.createCompositeInterface( - ClassUtils.toClassArray(ifcs), targetClass.getClassLoader()); - targetMethod = ClassUtils.getMostSpecificMethod(targetMethod, compositeInterface); + try { + Class compositeInterface = ClassUtils.createCompositeInterface( + ClassUtils.toClassArray(ifcs), targetClass.getClassLoader()); + targetMethod = ClassUtils.getMostSpecificMethod(targetMethod, compositeInterface); + } + catch (IllegalArgumentException ex) { + // Implemented interfaces probably expose conflicting method signatures... + // Proceed with original target method. + } } } return getShadowMatch(targetMethod, method); @@ -561,6 +566,19 @@ public String toString() { return sb.toString(); } + //--------------------------------------------------------------------- + // Serialization support + //--------------------------------------------------------------------- + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + // Rely on default serialization, just initialize state after deserialization. + ois.defaultReadObject(); + + // Initialize transient fields. + // pointcutExpression will be initialized lazily by checkReadyToMatch() + this.shadowMatchCache = new ConcurrentHashMap<>(32); + } + /** * Handler for the Spring-specific {@code bean()} pointcut designator @@ -657,20 +675,6 @@ private boolean matchesBean(String advisedBeanName) { } - //--------------------------------------------------------------------- - // Serialization support - //--------------------------------------------------------------------- - - private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { - // Rely on default serialization, just initialize state after deserialization. - ois.defaultReadObject(); - - // Initialize transient fields. - // pointcutExpression will be initialized lazily by checkReadyToMatch() - this.shadowMatchCache = new ConcurrentHashMap<>(32); - } - - private static class DefensiveShadowMatch implements ShadowMatch { private final ShadowMatch primary; diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJPointcutAdvisor.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJPointcutAdvisor.java index b4adec46c17a..108c0eeb8817 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJPointcutAdvisor.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJPointcutAdvisor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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 @@ public class AspectJPointcutAdvisor implements PointcutAdvisor, Ordered { /** - * Create a new AspectJPointcutAdvisor for the given advice + * Create a new AspectJPointcutAdvisor for the given advice. * @param advice the AbstractAspectJAdvice to wrap */ public AspectJPointcutAdvisor(AbstractAspectJAdvice advice) { 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 0fe055967939..ce06f9cde00b 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-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. @@ -27,27 +27,31 @@ * * @author Rod Johnson * @author Ramnivas Laddad + * @author Juergen Hoeller * @since 2.0 */ public abstract class AspectJProxyUtils { /** - * Add special advisors if necessary to work with a proxy chain that contains AspectJ advisors. - * This will expose the current Spring AOP invocation (necessary for some AspectJ pointcut matching) - * and make available the current AspectJ JoinPoint. The call will have no effect if there are no - * AspectJ advisors in the advisor chain. - * @param advisors Advisors available - * @return {@code true} if any special {@link Advisor Advisors} were added, otherwise {@code false}. + * Add special advisors if necessary to work with a proxy chain that contains AspectJ advisors: + * concretely, {@link ExposeInvocationInterceptor} at the beginning of the list. + *

This will expose the current Spring AOP invocation (necessary for some AspectJ pointcut + * matching) and make available the current AspectJ JoinPoint. The call will have no effect + * if there are no AspectJ advisors in the advisor chain. + * @param advisors the advisors available + * @return {@code true} if an {@link ExposeInvocationInterceptor} was added to the list, + * otherwise {@code false} */ public static boolean makeAdvisorChainAspectJCapableIfNecessary(List advisors) { // Don't add advisors to an empty list; may indicate that proxying is just not required if (!advisors.isEmpty()) { boolean foundAspectJAdvice = false; for (Advisor advisor : advisors) { - // Be careful not to get the Advice without a guard, as - // this might eagerly instantiate a non-singleton AspectJ aspect + // Be careful not to get the Advice without a guard, as this might eagerly + // instantiate a non-singleton AspectJ aspect... if (isAspectJAdvice(advisor)) { foundAspectJAdvice = true; + break; } } if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) { @@ -66,7 +70,7 @@ private static boolean isAspectJAdvice(Advisor advisor) { return (advisor instanceof InstantiationModelAwarePointcutAdvisor || advisor.getAdvice() instanceof AbstractAspectJAdvice || (advisor instanceof PointcutAdvisor && - ((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut)); + ((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut)); } } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/DeclareParentsAdvisor.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/DeclareParentsAdvisor.java index d2a607f54e2c..aa020a0756e9 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/DeclareParentsAdvisor.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/DeclareParentsAdvisor.java @@ -65,7 +65,7 @@ public DeclareParentsAdvisor(Class interfaceType, String typePattern, Object /** * Private constructor to share common code between impl-based delegate and reference-based delegate - * (cannot use method such as init() to share common code, due the use of final fields) + * (cannot use method such as init() to share common code, due the use of final fields). * @param interfaceType static field defining the introduction * @param typePattern type pattern the introduction is restricted to * @param interceptor the delegation advice as {@link IntroductionInterceptor} @@ -76,7 +76,7 @@ private DeclareParentsAdvisor(Class interfaceType, String typePattern, Introd // Excludes methods implemented. ClassFilter typePatternFilter = new TypePatternClassFilter(typePattern); - ClassFilter exclusion = (clazz -> !introducedInterface.isAssignableFrom(clazz)); + ClassFilter exclusion = (clazz -> !this.introducedInterface.isAssignableFrom(clazz)); this.typePatternClassFilter = ClassFilters.intersection(typePatternFilter, exclusion); } 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 23d3438e2cd0..e31b0129dcfd 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 @@ -58,11 +58,11 @@ public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint, @Nullable private Object[] args; - /** Lazily initialized signature object */ + /** Lazily initialized signature object. */ @Nullable private Signature signature; - /** Lazily initialized source location object */ + /** Lazily initialized source location object. */ @Nullable private SourceLocation sourceLocation; diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/RuntimeTestWalker.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/RuntimeTestWalker.java index 450c4b9201d1..59d243764e50 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/RuntimeTestWalker.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/RuntimeTestWalker.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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 @@ * migrate to {@code ShadowMatch.getVariablesInvolvedInRuntimeTest()} * or some similar operation. * - *

See Bug 151593 + *

See Bug 151593 * * @author Adrian Colyer * @author Ramnivas Laddad diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java index 70b928ab2fb4..c5463b146176 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,6 @@ import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.Nullable; -import org.springframework.util.StringUtils; /** * Abstract base class for factories that can create Spring AOP Advisors @@ -60,8 +59,11 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac private static final String AJC_MAGIC = "ajc$"; + private static final Class[] ASPECTJ_ANNOTATION_CLASSES = new Class[] { + Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class}; - /** Logger available to subclasses */ + + /** Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); protected final ParameterNameDiscoverer parameterNameDiscoverer = new AspectJAnnotationParameterNameDiscoverer(); @@ -123,15 +125,13 @@ public void validate(Class aspectClass) throws AopConfigException { /** * Find and return the first AspectJ annotation on the given method - * (there should only be one anyway...) + * (there should only be one anyway...). */ @SuppressWarnings("unchecked") @Nullable protected static AspectJAnnotation findAspectJAnnotationOnMethod(Method method) { - Class[] classesToLookFor = new Class[] { - Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class}; - for (Class c : classesToLookFor) { - AspectJAnnotation foundAnnotation = findAnnotation(method, (Class) c); + for (Class clazz : ASPECTJ_ANNOTATION_CLASSES) { + AspectJAnnotation foundAnnotation = findAnnotation(method, (Class) clazz); if (foundAnnotation != null) { return foundAnnotation; } @@ -151,34 +151,34 @@ private static AspectJAnnotation findAnnotation(Method } + /** + * Enum for AspectJ annotation types. + * @see AspectJAnnotation#getAnnotationType() + */ protected enum AspectJAnnotationType { - AtPointcut, - AtBefore, - AtAfter, - AtAfterReturning, - AtAfterThrowing, - AtAround + AtPointcut, AtAround, AtBefore, AtAfter, AtAfterReturning, AtAfterThrowing } /** * Class modelling an AspectJ annotation, exposing its type enumeration and * pointcut String. + * @param the annotation type */ protected static class AspectJAnnotation { - private static final String[] EXPRESSION_PROPERTIES = new String[] {"value", "pointcut"}; + private static final String[] EXPRESSION_ATTRIBUTES = new String[] {"pointcut", "value"}; - private static Map, AspectJAnnotationType> annotationTypes = new HashMap<>(); + private static Map, AspectJAnnotationType> annotationTypeMap = new HashMap<>(8); static { - annotationTypes.put(Pointcut.class,AspectJAnnotationType.AtPointcut); - annotationTypes.put(After.class,AspectJAnnotationType.AtAfter); - annotationTypes.put(AfterReturning.class,AspectJAnnotationType.AtAfterReturning); - annotationTypes.put(AfterThrowing.class,AspectJAnnotationType.AtAfterThrowing); - annotationTypes.put(Around.class,AspectJAnnotationType.AtAround); - annotationTypes.put(Before.class,AspectJAnnotationType.AtBefore); + annotationTypeMap.put(Pointcut.class, AspectJAnnotationType.AtPointcut); + annotationTypeMap.put(Around.class, AspectJAnnotationType.AtAround); + annotationTypeMap.put(Before.class, AspectJAnnotationType.AtBefore); + annotationTypeMap.put(After.class, AspectJAnnotationType.AtAfter); + annotationTypeMap.put(AfterReturning.class, AspectJAnnotationType.AtAfterReturning); + annotationTypeMap.put(AfterThrowing.class, AspectJAnnotationType.AtAfterThrowing); } private final A annotation; @@ -192,39 +192,31 @@ protected static class AspectJAnnotation { public AspectJAnnotation(A annotation) { this.annotation = annotation; this.annotationType = determineAnnotationType(annotation); - // We know these methods exist with the same name on each object, - // but need to invoke them reflectively as there isn't a common interface. try { this.pointcutExpression = resolveExpression(annotation); - this.argumentNames = (String) annotation.getClass().getMethod("argNames").invoke(annotation); + Object argNames = AnnotationUtils.getValue(annotation, "argNames"); + this.argumentNames = (argNames instanceof String ? (String) argNames : ""); } catch (Exception ex) { - throw new IllegalArgumentException(annotation + " cannot be an AspectJ annotation", ex); + throw new IllegalArgumentException(annotation + " is not a valid AspectJ annotation", ex); } } private AspectJAnnotationType determineAnnotationType(A annotation) { - for (Class type : annotationTypes.keySet()) { - if (type.isInstance(annotation)) { - return annotationTypes.get(type); - } + AspectJAnnotationType type = annotationTypeMap.get(annotation.annotationType()); + if (type != null) { + return type; } - throw new IllegalStateException("Unknown annotation type: " + annotation.toString()); + throw new IllegalStateException("Unknown annotation type: " + annotation); } - private String resolveExpression(A annotation) throws Exception { - for (String methodName : EXPRESSION_PROPERTIES) { - Method method; - try { - method = annotation.getClass().getDeclaredMethod(methodName); - } - catch (NoSuchMethodException ex) { - method = null; - } - if (method != null) { - String candidate = (String) method.invoke(annotation); - if (StringUtils.hasText(candidate)) { - return candidate; + private String resolveExpression(A annotation) { + for (String attributeName : EXPRESSION_ATTRIBUTES) { + Object val = AnnotationUtils.getValue(annotation, attributeName); + if (val instanceof String) { + String str = (String) val; + if (!str.isEmpty()) { + return str; } } } @@ -270,11 +262,11 @@ public String[] getParameterNames(Method method) { if (annotation == null) { return null; } - StringTokenizer strTok = new StringTokenizer(annotation.getArgumentNames(), ","); - if (strTok.countTokens() > 0) { - String[] names = new String[strTok.countTokens()]; + StringTokenizer nameTokens = new StringTokenizer(annotation.getArgumentNames(), ","); + if (nameTokens.countTokens() > 0) { + String[] names = new String[nameTokens.countTokens()]; for (int i = 0; i < names.length; i++) { - names[i] = strTok.nextToken(); + names[i] = nameTokens.nextToken(); } return names; } 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 5c4fe918c1f8..f1b7c86c58c0 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 @@ -49,7 +49,7 @@ @SuppressWarnings("serial") public class AspectJProxyFactory extends ProxyCreatorSupport { - /** Cache for singleton aspect instances */ + /** Cache for singleton aspect instances. */ private static final Map, Object> aspectCache = new ConcurrentHashMap<>(); private final AspectJAdvisorFactory aspectFactory = new ReflectiveAspectJAdvisorFactory(); 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 ccd581cf7b63..35edb5fd65f3 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,7 +55,7 @@ public class BeanFactoryAspectInstanceFactory implements MetadataAwareAspectInst * Create a BeanFactoryAspectInstanceFactory. AspectJ will be called to * introspect to create AJType metadata using the type returned for the * given bean name from the BeanFactory. - * @param beanFactory BeanFactory to obtain instance(s) from + * @param beanFactory the BeanFactory to obtain instance(s) from * @param name name of the bean */ public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name) { @@ -66,7 +66,7 @@ public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name) { * Create a BeanFactoryAspectInstanceFactory, providing a type that AspectJ should * introspect to create AJType metadata. Use if the BeanFactory may consider the type * to be a subclass (as when using CGLIB), and the information should relate to a superclass. - * @param beanFactory BeanFactory to obtain instance(s) from + * @param beanFactory the BeanFactory to obtain instance(s) from * @param name the name of the bean * @param type the type that should be introspected by AspectJ * ({@code null} indicates resolution through {@link BeanFactory#getType} via the bean name) 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 3a1a8575ee56..71ccb63a53bb 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-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,8 +16,8 @@ package org.springframework.aop.aspectj.annotation; +import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -87,8 +87,8 @@ public List buildAspectJAdvisors() { synchronized (this) { aspectNames = this.aspectBeanNames; if (aspectNames == null) { - List advisors = new LinkedList<>(); - aspectNames = new LinkedList<>(); + List advisors = new ArrayList<>(); + aspectNames = new ArrayList<>(); String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); for (String beanName : beanNames) { @@ -138,7 +138,7 @@ public List buildAspectJAdvisors() { if (aspectNames.isEmpty()) { return Collections.emptyList(); } - List advisors = new LinkedList<>(); + List advisors = new ArrayList<>(); for (String aspectName : aspectNames) { List cachedAdvisors = this.advisorsCache.get(aspectName); if (cachedAdvisors != null) { diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/InstantiationModelAwarePointcutAdvisorImpl.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/InstantiationModelAwarePointcutAdvisorImpl.java index d6e06e5bc77f..db2c9f400737 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/InstantiationModelAwarePointcutAdvisorImpl.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/InstantiationModelAwarePointcutAdvisorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,7 @@ * @since 2.0 */ @SuppressWarnings("serial") -class InstantiationModelAwarePointcutAdvisorImpl +final class InstantiationModelAwarePointcutAdvisorImpl implements InstantiationModelAwarePointcutAdvisor, AspectJPrecedenceInformation, Serializable { private static final Advice EMPTY_ADVICE = new Advice() {}; @@ -116,29 +116,22 @@ public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut decl /** - * The pointcut for Spring AOP to use. Actual behaviour of the pointcut will change - * depending on the state of the advice. + * The pointcut for Spring AOP to use. + * Actual behaviour of the pointcut will change depending on the state of the advice. */ @Override public Pointcut getPointcut() { return this.pointcut; } - /** - * This is only of interest for Spring AOP: AspectJ instantiation semantics - * are much richer. In AspectJ terminology, all a return of {@code true} - * means here is that the aspect is not a SINGLETON. - */ @Override - public boolean isPerInstance() { - return (getAspectMetadata().getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON); + public boolean isLazy() { + return this.lazy; } - /** - * Return the AspectJ AspectMetadata for this advisor. - */ - public AspectMetadata getAspectMetadata() { - return this.aspectInstanceFactory.getAspectMetadata(); + @Override + public synchronized boolean isAdviceInstantiated() { + return (this.instantiatedAdvice != null); } /** @@ -152,21 +145,27 @@ public synchronized Advice getAdvice() { return this.instantiatedAdvice; } - @Override - public boolean isLazy() { - return this.lazy; + private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) { + Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut, + this.aspectInstanceFactory, this.declarationOrder, this.aspectName); + return (advice != null ? advice : EMPTY_ADVICE); } + /** + * This is only of interest for Spring AOP: AspectJ instantiation semantics + * are much richer. In AspectJ terminology, all a return of {@code true} + * means here is that the aspect is not a SINGLETON. + */ @Override - public synchronized boolean isAdviceInstantiated() { - return (this.instantiatedAdvice != null); + public boolean isPerInstance() { + return (getAspectMetadata().getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON); } - - private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) { - Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut, - this.aspectInstanceFactory, this.declarationOrder, this.aspectName); - return (advice != null ? advice : EMPTY_ADVICE); + /** + * Return the AspectJ AspectMetadata for this advisor. + */ + public AspectMetadata getAspectMetadata() { + return this.aspectInstanceFactory.getAspectMetadata(); } public MetadataAwareAspectInstanceFactory getAspectInstanceFactory() { @@ -221,33 +220,26 @@ private void determineAdviceType() { } else { switch (aspectJAnnotation.getAnnotationType()) { - case AtAfter: - case AtAfterReturning: - case AtAfterThrowing: - this.isAfterAdvice = true; - this.isBeforeAdvice = false; - break; - case AtAround: case AtPointcut: - this.isAfterAdvice = false; + case AtAround: this.isBeforeAdvice = false; + this.isAfterAdvice = false; break; case AtBefore: - this.isAfterAdvice = false; this.isBeforeAdvice = true; + this.isAfterAdvice = false; + break; + case AtAfter: + case AtAfterReturning: + case AtAfterThrowing: + this.isBeforeAdvice = false; + this.isAfterAdvice = true; + break; } } } - @Override - public String toString() { - return "InstantiationModelAwarePointcutAdvisor: expression [" + getDeclaredPointcut().getExpression() + - "]; advice method [" + this.aspectJAdviceMethod + "]; perClauseKind=" + - this.aspectInstanceFactory.getAspectMetadata().getAjType().getPerClause().getKind(); - - } - private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException { inputStream.defaultReadObject(); try { @@ -258,13 +250,20 @@ private void readObject(ObjectInputStream inputStream) throws IOException, Class } } + @Override + public String toString() { + return "InstantiationModelAwarePointcutAdvisor: expression [" + getDeclaredPointcut().getExpression() + + "]; advice method [" + this.aspectJAdviceMethod + "]; perClauseKind=" + + this.aspectInstanceFactory.getAspectMetadata().getAjType().getPerClause().getKind(); + } + /** * Pointcut implementation that changes its behaviour when the advice is instantiated. - * Note that this is a dynamic pointcut. Otherwise it might - * be optimized out if it does not at first match statically. + * Note that this is a dynamic pointcut; otherwise it might be optimized out + * if it does not at first match statically. */ - private class PerTargetInstantiationModelPointcut extends DynamicMethodMatcherPointcut { + private final class PerTargetInstantiationModelPointcut extends DynamicMethodMatcherPointcut { private final AspectJExpressionPointcut declaredPointcut; @@ -273,7 +272,7 @@ private class PerTargetInstantiationModelPointcut extends DynamicMethodMatcherPo @Nullable private LazySingletonAspectInstanceFactoryDecorator aspectInstanceFactory; - private PerTargetInstantiationModelPointcut(AspectJExpressionPointcut declaredPointcut, + public PerTargetInstantiationModelPointcut(AspectJExpressionPointcut declaredPointcut, Pointcut preInstantiationPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory) { this.declaredPointcut = declaredPointcut; @@ -284,14 +283,15 @@ private PerTargetInstantiationModelPointcut(AspectJExpressionPointcut declaredPo } @Override - public boolean matches(Method method, @Nullable Class targetClass) { - // We're either instantiated and matching on declared pointcut, or uninstantiated matching on either pointcut + public boolean matches(Method method, Class targetClass) { + // We're either instantiated and matching on declared pointcut, + // or uninstantiated matching on either pointcut... return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass)) || this.preInstantiationPointcut.getMethodMatcher().matches(method, targetClass); } @Override - public boolean matches(Method method, @Nullable Class targetClass, Object... args) { + public boolean matches(Method method, Class targetClass, Object... args) { // This can match only on declared pointcut. return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass)); } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/NotAnAtAspectException.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/NotAnAtAspectException.java index 1c45cbc2f652..57ca507d6f87 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/NotAnAtAspectException.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/NotAnAtAspectException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ @SuppressWarnings("serial") public class NotAnAtAspectException extends AopConfigException { - private Class nonAspectClass; + private final Class nonAspectClass; /** 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 1b4ea1ed7c22..34081d6a727f 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-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. @@ -20,8 +20,8 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Comparator; -import java.util.LinkedList; import java.util.List; import org.aopalliance.aop.Advice; @@ -121,7 +121,7 @@ public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstan MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); - List advisors = new LinkedList<>(); + List advisors = new ArrayList<>(); for (Method method : getAdvisorMethods(aspectClass)) { Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); if (advisor != null) { @@ -147,13 +147,13 @@ public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstan } private List getAdvisorMethods(Class aspectClass) { - final List methods = new LinkedList<>(); + 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); return methods; } @@ -246,6 +246,15 @@ public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut AbstractAspectJAdvice springAdvice; switch (aspectJAnnotation.getAnnotationType()) { + case AtPointcut: + if (logger.isDebugEnabled()) { + logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'"); + } + return null; + case AtAround: + springAdvice = new AspectJAroundAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); + break; case AtBefore: springAdvice = new AspectJMethodBeforeAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); @@ -270,15 +279,6 @@ public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); } break; - case AtAround: - springAdvice = new AspectJAroundAdvice( - candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); - break; - case AtPointcut: - if (logger.isDebugEnabled()) { - logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'"); - } - return null; default: throw new UnsupportedOperationException( "Unsupported advice type on method: " + candidateAdviceMethod); diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/package-info.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/package-info.java index f9f0f3d11f8e..b5cf52470045 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/package-info.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/package-info.java @@ -8,4 +8,4 @@ package org.springframework.aop.aspectj.annotation; import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; \ No newline at end of file +import org.springframework.lang.NonNullFields; diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/autoproxy/package-info.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/autoproxy/package-info.java index 5f5b7dedd4c3..d83cd88d541f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/autoproxy/package-info.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/autoproxy/package-info.java @@ -7,4 +7,4 @@ package org.springframework.aop.aspectj.autoproxy; import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; \ No newline at end of file +import org.springframework.lang.NonNullFields; diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/package-info.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/package-info.java index cf0e9a19e345..2ffe8b16438b 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/package-info.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/package-info.java @@ -13,4 +13,4 @@ package org.springframework.aop.aspectj; import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; \ No newline at end of file +import org.springframework.lang.NonNullFields; diff --git a/spring-aop/src/main/java/org/springframework/aop/config/AdvisorComponentDefinition.java b/spring-aop/src/main/java/org/springframework/aop/config/AdvisorComponentDefinition.java index 03034f48fbef..e812e73abffa 100644 --- a/spring-aop/src/main/java/org/springframework/aop/config/AdvisorComponentDefinition.java +++ b/spring-aop/src/main/java/org/springframework/aop/config/AdvisorComponentDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ public class AdvisorComponentDefinition extends AbstractComponentDefinition { public AdvisorComponentDefinition(String advisorBeanName, BeanDefinition advisorDefinition) { - this(advisorBeanName, advisorDefinition, null); + this(advisorBeanName, advisorDefinition, null); } public AdvisorComponentDefinition( diff --git a/spring-aop/src/main/java/org/springframework/aop/config/AopConfigUtils.java b/spring-aop/src/main/java/org/springframework/aop/config/AopConfigUtils.java index 2020c9fa7397..8335dbf1ae5f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/config/AopConfigUtils.java +++ b/spring-aop/src/main/java/org/springframework/aop/config/AopConfigUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,11 +32,10 @@ /** * Utility class for handling registration of AOP auto-proxy creators. * - *

Only a single auto-proxy creator can be registered yet multiple concrete - * implementations are available. Therefore this class wraps a simple escalation - * protocol, allowing classes to request a particular auto-proxy creator and know - * that class, {@code or a subclass thereof}, will eventually be resident - * in the application context. + *

Only a single auto-proxy creator should be registered yet multiple concrete + * implementations are available. This class provides a simple escalation protocol, + * allowing a caller to request a particular auto-proxy creator and know that creator, + * or a more capable variant thereof, will be registered as a post-processor. * * @author Rob Harrop * @author Juergen Hoeller @@ -55,12 +54,10 @@ public abstract class AopConfigUtils { /** * Stores the auto proxy creator classes in escalation order. */ - private static final List> APC_PRIORITY_LIST = new ArrayList<>(); + private static final List> APC_PRIORITY_LIST = new ArrayList<>(3); - /** - * Setup the escalation list. - */ static { + // Set up the escalation list... APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); @@ -73,8 +70,8 @@ public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionR } @Nullable - public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, - @Nullable Object source) { + public static BeanDefinition registerAutoProxyCreatorIfNecessary( + BeanDefinitionRegistry registry, @Nullable Object source) { return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source); } @@ -85,8 +82,8 @@ public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefi } @Nullable - public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, - @Nullable Object source) { + public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary( + BeanDefinitionRegistry registry, @Nullable Object source) { return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source); } @@ -97,8 +94,8 @@ public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessar } @Nullable - public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, - @Nullable Object source) { + public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( + BeanDefinitionRegistry registry, @Nullable Object source) { return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); } @@ -118,8 +115,8 @@ public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry reg } @Nullable - private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, - @Nullable Object source) { + private static BeanDefinition registerOrEscalateApcAsRequired( + Class cls, BeanDefinitionRegistry registry, @Nullable Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); diff --git a/spring-aop/src/main/java/org/springframework/aop/config/AopNamespaceUtils.java b/spring-aop/src/main/java/org/springframework/aop/config/AopNamespaceUtils.java index 7276ec78436e..c5d6e6efb0cb 100644 --- a/spring-aop/src/main/java/org/springframework/aop/config/AopNamespaceUtils.java +++ b/spring-aop/src/main/java/org/springframework/aop/config/AopNamespaceUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,11 +28,11 @@ * Utility class for handling registration of auto-proxy creators used internally * by the '{@code aop}' namespace tags. * - *

Only a single auto-proxy creator can be registered and multiple tags may wish - * to register different concrete implementations. As such this class delegates to - * {@link AopConfigUtils} which wraps a simple escalation protocol. Therefore classes - * may request a particular auto-proxy creator and know that class, or a subclass - * thereof, will eventually be resident in the application context. + *

Only a single auto-proxy creator should be registered and multiple configuration + * elements may wish to register different concrete implementations. As such this class + * delegates to {@link AopConfigUtils} which provides a simple escalation protocol. + * Callers may request a particular auto-proxy creator and know that creator, + * or a more capable variant thereof, will be registered as a post-processor. * * @author Rob Harrop * @author Juergen Hoeller @@ -95,9 +95,8 @@ private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) { if (beanDefinition != null) { - BeanComponentDefinition componentDefinition = - new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME); - parserContext.registerComponent(componentDefinition); + parserContext.registerComponent( + new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME)); } } diff --git a/spring-aop/src/main/java/org/springframework/aop/config/package-info.java b/spring-aop/src/main/java/org/springframework/aop/config/package-info.java index e5edac7411a9..b0d1010cb327 100644 --- a/spring-aop/src/main/java/org/springframework/aop/config/package-info.java +++ b/spring-aop/src/main/java/org/springframework/aop/config/package-info.java @@ -7,4 +7,4 @@ package org.springframework.aop.config; import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; \ No newline at end of file +import org.springframework.lang.NonNullFields; 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 4faca7ce2d4c..b08de06d5c3d 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-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,7 +63,7 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) { @Override public Object postProcessAfterInitialization(Object bean, String beanName) { - if (bean instanceof AopInfrastructureBean || this.advisor == null) { + if (this.advisor == null || bean instanceof AopInfrastructureBean) { // Ignore AOP infrastructure such as scoped proxies. return bean; } @@ -92,7 +92,7 @@ public Object postProcessAfterInitialization(Object bean, String beanName) { return proxyFactory.getProxy(getProxyClassLoader()); } - // No async proxy needed. + // No proxy needed. return bean; } @@ -160,7 +160,7 @@ protected ProxyFactory prepareProxyFactory(Object bean, String beanName) { * Subclasses may choose to implement this: for example, * to change the interfaces exposed. *

The default implementation is empty. - * @param proxyFactory ProxyFactory that is already configured with + * @param proxyFactory the ProxyFactory that is already configured with * target, advisor and interfaces and will be used to create the proxy * immediately after this method returns * @since 4.2.3 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 f26af4908802..6e18786d58b4 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,7 +54,7 @@ public abstract class AbstractSingletonProxyFactoryBean extends ProxyConfig @Nullable private Object[] postInterceptors; - /** Default is global AdvisorAdapterRegistry */ + /** Default is global AdvisorAdapterRegistry. */ private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance(); @Nullable 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 db01d75a1f60..c946034bdec2 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-2015 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -211,7 +211,7 @@ public interface Advised extends TargetClassAware { * or -1 if no such advice is an advice for this proxy. *

The return value of this method can be used to index into * the advisors array. - * @param advice AOP Alliance advice to search for + * @param advice the AOP Alliance advice to search for * @return index from 0 of this advice, or -1 if there's no such advice */ int indexOf(Advice advice); 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 93e22e368b58..54133402e6fb 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 @@ -22,7 +22,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -61,7 +60,7 @@ */ public class AdvisedSupport extends ProxyConfig implements Advised { - /** use serialVersionUID from Spring 2.0 for interoperability */ + /** use serialVersionUID from Spring 2.0 for interoperability. */ private static final long serialVersionUID = 2651364800145442165L; @@ -72,16 +71,16 @@ public class AdvisedSupport extends ProxyConfig implements Advised { public static final TargetSource EMPTY_TARGET_SOURCE = EmptyTargetSource.INSTANCE; - /** Package-protected to allow direct access for efficiency */ + /** Package-protected to allow direct access for efficiency. */ TargetSource targetSource = EMPTY_TARGET_SOURCE; - /** Whether the Advisors are already filtered for the specific target class */ + /** Whether the Advisors are already filtered for the specific target class. */ private boolean preFiltered = false; - /** The AdvisorChainFactory to use */ + /** The AdvisorChainFactory to use. */ AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory(); - /** Cache with Method as key and advisor chain List as value */ + /** Cache with Method as key and advisor chain List as value. */ private transient Map> methodCache; /** @@ -94,7 +93,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised { * List of Advisors. If an Advice is added, it will be wrapped * in an Advisor before being added to this List. */ - private List advisors = new LinkedList<>(); + private List advisors = new ArrayList<>(); /** * Array updated on changes to the advisors list, which is easier @@ -153,11 +152,12 @@ public TargetSource getTargetSource() { * @see #setTargetSource * @see #setTarget */ - public void setTargetClass(Class targetClass) { + public void setTargetClass(@Nullable Class targetClass) { this.targetSource = EmptyTargetSource.forClass(targetClass); } @Override + @Nullable public Class getTargetClass() { return this.targetSource.getTargetClass(); } @@ -474,7 +474,7 @@ public int countAdvicesOfType(@Nullable Class adviceClass) { * for the given method, based on this configuration. * @param method the proxied method * @param targetClass the target class - * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers) + * @return a List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers) */ public List getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class targetClass) { MethodCacheKey cacheKey = new MethodCacheKey(method); @@ -528,7 +528,7 @@ protected void copyConfigurationFrom(AdvisedSupport other, TargetSource targetSo /** * Build a configuration-only copy of this AdvisedSupport, - * replacing the TargetSource + * replacing the TargetSource. */ AdvisedSupport getConfigurationOnlyCopy() { AdvisedSupport copy = new AdvisedSupport(); diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/AdvisorChainFactory.java b/spring-aop/src/main/java/org/springframework/aop/framework/AdvisorChainFactory.java index 87c0a810eeb8..2640101b04b3 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/AdvisorChainFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/AdvisorChainFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2018 the original author 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,7 @@ public interface AdvisorChainFactory { * @param method the proxied method * @param targetClass the target class (may be {@code null} to indicate a proxy without * target object, in which case the method's declaring class is the next best option) - * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers) + * @return a List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers) */ List getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class targetClass); 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 d37f7a958d1d..23b992a454e5 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-2016 the original author or authors. + * Copyright 2002-2018 the original author 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 @@ * @author Juergen Hoeller * @since 13.03.2003 */ -public abstract class AopContext { +public final class AopContext { /** * ThreadLocal holder for AOP proxy associated with this thread. @@ -50,11 +50,15 @@ public abstract class AopContext { private static final ThreadLocal currentProxy = new NamedThreadLocal<>("Current AOP proxy"); + private AopContext() { + } + + /** * Try to return the current AOP proxy. This method is usable only if the * calling method has been invoked via AOP, and the AOP framework has been set * to expose proxies. Otherwise, this method will throw an IllegalStateException. - * @return Object the current AOP proxy (never returns {@code null}) + * @return the current AOP proxy (never returns {@code null}) * @throws IllegalStateException if the proxy cannot be found, because the * method was invoked outside an AOP invocation context, or because the * AOP framework has not been configured to expose the proxy 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 71b2bf1ddaa0..dd9a91b06cd3 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 @@ -93,14 +93,14 @@ class CglibAopProxy implements AopProxy, Serializable { private static final int INVOKE_HASHCODE = 6; - /** Logger available to subclasses; static to optimize serialization */ + /** Logger available to subclasses; static to optimize serialization. */ protected static final Log logger = LogFactory.getLog(CglibAopProxy.class); - /** Keeps track of the Classes that we have validated for final methods */ + /** Keeps track of the Classes that we have validated for final methods. */ private static final Map, Boolean> validatedClasses = new WeakHashMap<>(); - /** The configuration used to configure this proxy */ + /** The configuration used to configure this proxy. */ protected final AdvisedSupport advised; @Nullable @@ -109,10 +109,10 @@ class CglibAopProxy implements AopProxy, Serializable { @Nullable protected Class[] constructorArgTypes; - /** Dispatcher used for methods on Advised */ + /** Dispatcher used for methods on Advised. */ private final transient AdvisedDispatcher advisedDispatcher; - private transient Map fixedInterceptorMap = Collections.emptyMap(); + private transient Map fixedInterceptorMap = Collections.emptyMap(); private transient int fixedInterceptorOffset; @@ -157,8 +157,8 @@ public Object getProxy() { @Override public Object getProxy(@Nullable ClassLoader classLoader) { - if (logger.isDebugEnabled()) { - logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource()); + if (logger.isTraceEnabled()) { + logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource()); } try { @@ -259,16 +259,16 @@ private void doValidateClass(Class proxySuperClass, @Nullable ClassLoader pro if (!Modifier.isStatic(mod) && !Modifier.isPrivate(mod)) { if (Modifier.isFinal(mod)) { if (implementsInterface(method, ifcs)) { - logger.warn("Unable to proxy interface-implementing method [" + method + "] because " + + logger.info("Unable to proxy interface-implementing method [" + method + "] because " + "it is marked as final: Consider using interface-based JDK proxies instead!"); } - logger.info("Final method [" + method + "] cannot get proxied via CGLIB: " + + 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) && proxyClassLoader != null && proxySuperClass.getClassLoader() != proxyClassLoader) { - logger.info("Method [" + method + "] is package-visible across different ClassLoaders " + + logger.debug("Method [" + method + "] is package-visible across different ClassLoaders " + "and cannot get proxied via CGLIB: Declare this method as public or protected " + "if you need to support invocations through the proxy."); } @@ -291,20 +291,20 @@ private Callback[] getCallbacks(Class rootClass) throws Exception { // unadvised but can return this). May be required to expose the proxy. Callback targetInterceptor; if (exposeProxy) { - targetInterceptor = isStatic ? + targetInterceptor = (isStatic ? new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) : - new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()); + new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource())); } else { - targetInterceptor = isStatic ? + targetInterceptor = (isStatic ? new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) : - new DynamicUnadvisedInterceptor(this.advised.getTargetSource()); + new DynamicUnadvisedInterceptor(this.advised.getTargetSource())); } // Choose a "direct to target" dispatcher (used for // unadvised calls to static targets that cannot return this). - Callback targetDispatcher = isStatic ? - new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp(); + Callback targetDispatcher = (isStatic ? + new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp()); Callback[] mainCallbacks = new Callback[] { aopInterceptor, // for normal advice @@ -327,10 +327,11 @@ private Callback[] getCallbacks(Class rootClass) throws Exception { // TODO: small memory optimization here (can skip creation for methods with no advice) for (int x = 0; x < methods.length; x++) { - List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass); + Method method = methods[x]; + List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass); fixedCallbacks[x] = new FixedChainStaticTargetInterceptor( chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass()); - this.fixedInterceptorMap.put(methods[x].toString(), x); + this.fixedInterceptorMap.put(method, x); } // Now copy both the callbacks from mainCallbacks @@ -723,17 +724,20 @@ public int hashCode() { */ private static class CglibMethodInvocation extends ReflectiveMethodInvocation { + @Nullable private final MethodProxy methodProxy; - private final boolean publicMethod; - public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method, Object[] arguments, @Nullable Class targetClass, List interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) { super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers); - this.methodProxy = methodProxy; - this.publicMethod = Modifier.isPublic(method.getModifiers()); + + // Only use method proxy for public methods not derived from java.lang.Object + this.methodProxy = (Modifier.isPublic(method.getModifiers()) && + method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) && + !AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method) ? + methodProxy : null); } /** @@ -742,7 +746,7 @@ public CglibMethodInvocation(Object proxy, @Nullable Object target, Method metho */ @Override protected Object invokeJoinpoint() throws Throwable { - if (this.publicMethod && getMethod().getDeclaringClass() != Object.class) { + if (this.methodProxy != null) { return this.methodProxy.invoke(this.target, this.arguments); } else { @@ -759,12 +763,12 @@ private static class ProxyCallbackFilter implements CallbackFilter { private final AdvisedSupport advised; - private final Map fixedInterceptorMap; + private final Map fixedInterceptorMap; private final int fixedInterceptorOffset; public ProxyCallbackFilter( - AdvisedSupport advised, Map fixedInterceptorMap, int fixedInterceptorOffset) { + AdvisedSupport advised, Map fixedInterceptorMap, int fixedInterceptorOffset) { this.advised = advised; this.fixedInterceptorMap = fixedInterceptorMap; @@ -795,7 +799,7 @@ public ProxyCallbackFilter( *
For advised methods:
*
If the target is static and the advice chain is frozen then a * FixedChainStaticTargetInterceptor specific to the method is used to - * invoke the advice chain. Otherwise a DyanmicAdvisedInterceptor is + * invoke the advice chain. Otherwise a DynamicAdvisedInterceptor is * used.
*
For non-advised methods:
*
Where it can be determined that the method will not return {@code this} @@ -810,24 +814,28 @@ public ProxyCallbackFilter( @Override public int accept(Method method) { if (AopUtils.isFinalizeMethod(method)) { - logger.debug("Found finalize() method - using NO_OVERRIDE"); + logger.trace("Found finalize() method - using NO_OVERRIDE"); return NO_OVERRIDE; } if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { - if (logger.isDebugEnabled()) { - logger.debug("Method is declared on Advised interface: " + method); + if (logger.isTraceEnabled()) { + logger.trace("Method is declared on Advised interface: " + method); } return DISPATCH_ADVISED; } // We must always proxy equals, to direct calls to this. if (AopUtils.isEqualsMethod(method)) { - logger.debug("Found 'equals' method: " + method); + if (logger.isTraceEnabled()) { + logger.trace("Found 'equals' method: " + method); + } return INVOKE_EQUALS; } // We must always calculate hashCode based on the proxy. if (AopUtils.isHashCodeMethod(method)) { - logger.debug("Found 'hashCode' method: " + method); + if (logger.isTraceEnabled()) { + logger.trace("Found 'hashCode' method: " + method); + } return INVOKE_HASHCODE; } Class targetClass = this.advised.getTargetClass(); @@ -840,25 +848,25 @@ public int accept(Method method) { if (haveAdvice || !isFrozen) { // If exposing the proxy, then AOP_PROXY must be used. if (exposeProxy) { - if (logger.isDebugEnabled()) { - logger.debug("Must expose proxy on advised method: " + method); + if (logger.isTraceEnabled()) { + logger.trace("Must expose proxy on advised method: " + method); } return AOP_PROXY; } - String key = method.toString(); + 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 (logger.isDebugEnabled()) { - logger.debug("Method has advice and optimizations are enabled: " + 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); return (index + this.fixedInterceptorOffset); } else { - if (logger.isDebugEnabled()) { - logger.debug("Unable to apply any optimizations to advised method: " + method); + if (logger.isTraceEnabled()) { + logger.trace("Unable to apply any optimizations to advised method: " + method); } return AOP_PROXY; } @@ -874,15 +882,15 @@ public int accept(Method method) { } Class returnType = method.getReturnType(); if (targetClass != null && returnType.isAssignableFrom(targetClass)) { - if (logger.isDebugEnabled()) { - logger.debug("Method return type is assignable from target type and " + + if (logger.isTraceEnabled()) { + logger.trace("Method return type is assignable from target type and " + "may therefore return 'this' - using INVOKE_TARGET: " + method); } return INVOKE_TARGET; } else { - if (logger.isDebugEnabled()) { - logger.debug("Method return type ensures 'this' cannot be returned - " + + if (logger.isTraceEnabled()) { + logger.trace("Method return type ensures 'this' cannot be returned - " + "using DISPATCH_TARGET: " + method); } return DISPATCH_TARGET; diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java index decf2e55a3e1..31438c78f878 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author 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.aop.Advisor; import org.springframework.aop.IntroductionAdvisor; +import org.springframework.aop.IntroductionAwareMethodMatcher; import org.springframework.aop.MethodMatcher; import org.springframework.aop.PointcutAdvisor; import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry; import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry; -import org.springframework.aop.support.MethodMatchers; import org.springframework.lang.Nullable; /** @@ -53,19 +53,30 @@ public List getInterceptorsAndDynamicInterceptionAdvice( // This is somewhat tricky... We have to process introductions first, // but we need to preserve order in the ultimate list. - List interceptorList = new ArrayList<>(config.getAdvisors().length); - Class actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); - boolean hasIntroductions = hasMatchingIntroductions(config, actualClass); AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); + Advisor[] advisors = config.getAdvisors(); + List interceptorList = new ArrayList<>(advisors.length); + Class actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); + Boolean hasIntroductions = null; - for (Advisor advisor : config.getAdvisors()) { + for (Advisor advisor : advisors) { if (advisor instanceof PointcutAdvisor) { // Add it conditionally. PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor; if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { - MethodInterceptor[] interceptors = registry.getInterceptors(advisor); MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher(); - if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) { + boolean match; + if (mm instanceof IntroductionAwareMethodMatcher) { + if (hasIntroductions == null) { + hasIntroductions = hasMatchingIntroductions(advisors, actualClass); + } + match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions); + } + else { + match = mm.matches(method, actualClass); + } + if (match) { + MethodInterceptor[] interceptors = registry.getInterceptors(advisor); if (mm.isRuntime()) { // Creating a new object instance in the getInterceptors() method // isn't a problem as we normally cache created chains. @@ -98,9 +109,8 @@ else if (advisor instanceof IntroductionAdvisor) { /** * Determine whether the Advisors contain matching introductions. */ - private static boolean hasMatchingIntroductions(Advised config, Class actualClass) { - for (int i = 0; i < config.getAdvisors().length; i++) { - Advisor advisor = config.getAdvisors()[i]; + private static boolean hasMatchingIntroductions(Advisor[] advisors, Class actualClass) { + for (Advisor advisor : advisors) { if (advisor instanceof IntroductionAdvisor) { IntroductionAdvisor ia = (IntroductionAdvisor) advisor; if (ia.getClassFilter().matches(actualClass)) { 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 eb02093c2043..f31cd878e6b0 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-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,7 +63,7 @@ */ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable { - /** use serialVersionUID from Spring 1.2 for interoperability */ + /** use serialVersionUID from Spring 1.2 for interoperability. */ private static final long serialVersionUID = 5531744639992436476L; @@ -76,10 +76,10 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa * This way, we can also more easily take advantage of minor optimizations in each class. */ - /** We use a static Log to avoid serialization issues */ + /** We use a static Log to avoid serialization issues. */ private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class); - /** Config used to configure this proxy */ + /** Config used to configure this proxy. */ private final AdvisedSupport advised; /** @@ -115,8 +115,8 @@ public Object getProxy() { @Override public Object getProxy(@Nullable ClassLoader classLoader) { - if (logger.isDebugEnabled()) { - logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); + if (logger.isTraceEnabled()) { + logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ObjenesisCglibAopProxy.java b/spring-aop/src/main/java/org/springframework/aop/framework/ObjenesisCglibAopProxy.java index 89e47fe29530..02c1d4dfe898 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/ObjenesisCglibAopProxy.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/ObjenesisCglibAopProxy.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. @@ -29,7 +29,7 @@ /** * Objenesis-based extension of {@link CglibAopProxy} to create proxy instances - * without invoking the constructor of the class. + * without invoking the constructor of the class. Used by default as of Spring 4. * * @author Oliver Gierke * @author Juergen Hoeller @@ -53,7 +53,6 @@ public ObjenesisCglibAopProxy(AdvisedSupport config) { @Override - @SuppressWarnings("unchecked") protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) { Class proxyClass = enhancer.createClass(); Object proxyInstance = null; diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyConfig.java b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyConfig.java index fc7ae7b878aa..9847b0de4ed3 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyConfig.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author 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,7 +30,7 @@ */ public class ProxyConfig implements Serializable { - /** use serialVersionUID from Spring 1.2 for interoperability */ + /** use serialVersionUID from Spring 1.2 for interoperability. */ private static final long serialVersionUID = -8409359707199703185L; 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 a0852f23dfa8..59ae03651c14 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-2016 the original author or authors. + * Copyright 2002-2018 the original author 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,9 +34,9 @@ public class ProxyCreatorSupport extends AdvisedSupport { private AopProxyFactory aopProxyFactory; - private List listeners = new LinkedList<>(); + private final List listeners = new LinkedList<>(); - /** Set to true when the first AOP proxy has been created */ + /** 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 a806b4037e8b..b91134a2bb5a 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-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -124,10 +124,10 @@ public class ProxyFactoryBean extends ProxyCreatorSupport @Nullable private transient BeanFactory beanFactory; - /** Whether the advisor chain has already been initialized */ + /** Whether the advisor chain has already been initialized. */ private boolean advisorChainInitialized = false; - /** If this is a singleton, the cached singleton proxy instance */ + /** If this is a singleton, the cached singleton proxy instance. */ @Nullable private Object singletonInstance; @@ -255,7 +255,7 @@ public Object getObject() throws BeansException { } else { if (this.targetName == null) { - logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " + + logger.info("Using non-singleton proxies with singleton targets is often undesirable. " + "Enable prototype proxies by setting the 'targetName' property."); } return newPrototypeInstance(); @@ -651,7 +651,7 @@ public PrototypePlaceholderAdvisor(String beanName) { } public String getBeanName() { - return beanName; + return this.beanName; } @Override 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 c8a2ece27312..3a2134fb4fae 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -170,7 +170,8 @@ public Object proceed() throws Throwable { // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; - if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { + Class targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); + if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AdvisorAdapterRegistry.java b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AdvisorAdapterRegistry.java index 9eacf479bada..4183ec8c4fea 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AdvisorAdapterRegistry.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AdvisorAdapterRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author 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,15 +31,15 @@ public interface AdvisorAdapterRegistry { /** - * Return an Advisor wrapping the given advice. + * Return an {@link Advisor} wrapping the given advice. *

Should by default at least support * {@link org.aopalliance.intercept.MethodInterceptor}, * {@link org.springframework.aop.MethodBeforeAdvice}, * {@link org.springframework.aop.AfterReturningAdvice}, * {@link org.springframework.aop.ThrowsAdvice}. - * @param advice object that should be an advice - * @return an Advisor wrapping the given advice. Never returns {@code null}. - * If the advice parameter is an Advisor, return it. + * @param advice an object that should be an advice + * @return an Advisor wrapping the given advice (never {@code null}; + * if the advice parameter is an Advisor, it is to be returned as-is) * @throws UnknownAdviceTypeException if no registered advisor adapter * can wrap the supposed advice */ @@ -48,21 +48,20 @@ public interface AdvisorAdapterRegistry { /** * Return an array of AOP Alliance MethodInterceptors to allow use of the * given Advisor in an interception-based framework. - *

Don't worry about the pointcut associated with the Advisor, - * if it's a PointcutAdvisor: just return an interceptor. - * @param advisor Advisor to find an interceptor for + *

Don't worry about the pointcut associated with the {@link Advisor}, if it is + * a {@link org.springframework.aop.PointcutAdvisor}: just return an interceptor. + * @param advisor the Advisor to find an interceptor for * @return an array of MethodInterceptors to expose this Advisor's behavior * @throws UnknownAdviceTypeException if the Advisor type is - * not understood by any registered AdvisorAdapter. + * not understood by any registered AdvisorAdapter */ MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException; /** - * Register the given AdvisorAdapter. Note that it is not necessary to register + * Register the given {@link AdvisorAdapter}. Note that it is not necessary to register * adapters for an AOP Alliance Interceptors or Spring Advices: these must be - * automatically recognized by an AdvisorAdapterRegistry implementation. - * @param adapter AdvisorAdapter that understands a particular Advisor - * or Advice types + * automatically recognized by an {@code AdvisorAdapterRegistry} implementation. + * @param adapter an AdvisorAdapter that understands particular Advisor or Advice types */ void registerAdvisorAdapter(AdvisorAdapter adapter); 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 34fd15378e4f..82e5856250b6 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-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,6 +31,8 @@ * to use this class directly. * * @author Rod Johnson + * @see MethodBeforeAdviceInterceptor + * @see ThrowsAdviceInterceptor */ @SuppressWarnings("serial") public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable { @@ -47,6 +49,7 @@ public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) { this.advice = advice; } + @Override public Object invoke(MethodInvocation mi) throws Throwable { Object retVal = mi.proceed(); diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/GlobalAdvisorAdapterRegistry.java b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/GlobalAdvisorAdapterRegistry.java index 0cd2d7ef9dbf..f9a677c5ad6a 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/GlobalAdvisorAdapterRegistry.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/GlobalAdvisorAdapterRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author 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,11 @@ * @author Phillip Webb * @see DefaultAdvisorAdapterRegistry */ -public abstract class GlobalAdvisorAdapterRegistry { +public final class GlobalAdvisorAdapterRegistry { + + private GlobalAdvisorAdapterRegistry() { + } + /** * Keep track of a single instance so we can return it to classes that request it. 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 9ede67ef9818..a58e27cdc366 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-2012 the original author or authors. + * Copyright 2002-2018 the original author 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.aop.BeforeAdvice; import org.springframework.aop.MethodBeforeAdvice; import org.springframework.util.Assert; @@ -30,11 +31,13 @@ * to use this class directly. * * @author Rod Johnson + * @see AfterReturningAdviceInterceptor + * @see ThrowsAdviceInterceptor */ @SuppressWarnings("serial") -public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable { +public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable { - private MethodBeforeAdvice advice; + private final MethodBeforeAdvice advice; /** @@ -46,6 +49,7 @@ public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) { this.advice = advice; } + @Override public Object invoke(MethodInvocation mi) throws Throwable { this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); 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 4bcd32647ebe..bea87cfd68f3 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-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,6 +51,8 @@ * * @author Rod Johnson * @author Juergen Hoeller + * @see MethodBeforeAdviceInterceptor + * @see AfterReturningAdviceInterceptor */ public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice { @@ -61,15 +63,14 @@ public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice { private final Object throwsAdvice; - /** Methods on throws advice, keyed by exception class */ + /** Methods on throws advice, keyed by exception class. */ private final Map, Method> exceptionHandlerMap = new HashMap<>(); /** * Create a new ThrowsAdviceInterceptor for the given ThrowsAdvice. - * @param throwsAdvice the advice object that defines the exception - * handler methods (usually a {@link org.springframework.aop.ThrowsAdvice} - * implementation) + * @param throwsAdvice the advice object that defines the exception handler methods + * (usually a {@link org.springframework.aop.ThrowsAdvice} implementation) */ public ThrowsAdviceInterceptor(Object throwsAdvice) { Assert.notNull(throwsAdvice, "Advice must not be null"); @@ -78,13 +79,14 @@ public ThrowsAdviceInterceptor(Object throwsAdvice) { Method[] methods = throwsAdvice.getClass().getMethods(); for (Method method : methods) { if (method.getName().equals(AFTER_THROWING) && - (method.getParameterCount() == 1 || method.getParameterCount() == 4) && - Throwable.class.isAssignableFrom(method.getParameterTypes()[method.getParameterCount() - 1]) - ) { - // Have an exception handler - this.exceptionHandlerMap.put(method.getParameterTypes()[method.getParameterCount() - 1], method); - if (logger.isDebugEnabled()) { - logger.debug("Found exception handler method: " + method); + (method.getParameterCount() == 1 || method.getParameterCount() == 4)) { + Class throwableParam = method.getParameterTypes()[method.getParameterCount() - 1]; + if (Throwable.class.isAssignableFrom(throwableParam)) { + // An exception handler to register... + this.exceptionHandlerMap.put(throwableParam, method); + if (logger.isDebugEnabled()) { + logger.debug("Found exception handler method on throws advice: " + method); + } } } } @@ -95,14 +97,33 @@ public ThrowsAdviceInterceptor(Object throwsAdvice) { } } + + /** + * Return the number of handler methods in this advice. + */ public int getHandlerMethodCount() { return this.exceptionHandlerMap.size(); } + + @Override + public Object invoke(MethodInvocation mi) throws Throwable { + try { + return mi.proceed(); + } + catch (Throwable ex) { + Method handlerMethod = getExceptionHandler(ex); + if (handlerMethod != null) { + invokeHandlerMethod(mi, ex, handlerMethod); + } + throw ex; + } + } + /** - * Determine the exception handle method. Can return null if not found. + * Determine the exception handle method for the given exception. * @param exception the exception thrown - * @return a handler for the given exception type + * @return a handler for the given exception type, or {@code null} if none found */ @Nullable private Method getExceptionHandler(Throwable exception) { @@ -115,30 +136,16 @@ private Method getExceptionHandler(Throwable exception) { exceptionClass = exceptionClass.getSuperclass(); handler = this.exceptionHandlerMap.get(exceptionClass); } - if (handler != null && logger.isDebugEnabled()) { - logger.debug("Found handler for exception of type [" + exceptionClass.getName() + "]: " + handler); + if (handler != null && logger.isTraceEnabled()) { + logger.trace("Found handler for exception of type [" + exceptionClass.getName() + "]: " + handler); } return handler; } - @Override - public Object invoke(MethodInvocation mi) throws Throwable { - try { - return mi.proceed(); - } - catch (Throwable ex) { - Method handlerMethod = getExceptionHandler(ex); - if (handlerMethod != null) { - invokeHandlerMethod(mi, ex, handlerMethod); - } - throw ex; - } - } - private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method method) throws Throwable { Object[] handlerArgs; if (method.getParameterCount() == 1) { - handlerArgs = new Object[] { ex }; + handlerArgs = new Object[] {ex}; } else { handlerArgs = new Object[] {mi.getMethod(), mi.getArguments(), mi.getThis(), ex}; diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/package-info.java b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/package-info.java index 9f7ca35fbad7..1925e47bfbc6 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/package-info.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/package-info.java @@ -14,4 +14,4 @@ package org.springframework.aop.framework.adapter; import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; \ No newline at end of file +import org.springframework.lang.NonNullFields; 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 2f59b4af88e5..9ca006be4285 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-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. @@ -31,8 +31,8 @@ * Generic auto proxy creator that builds AOP proxies for specific beans * based on detected Advisors for each bean. * - *

Subclasses must implement the abstract {@link #findCandidateAdvisors()} - * method to return a list of Advisors applying to any object. Subclasses can + *

Subclasses may override the {@link #findCandidateAdvisors()} method to + * return a custom list of Advisors applying to any object. Subclasses can * also override the inherited {@link #shouldSkip} method to exclude certain * objects from auto-proxying. * @@ -160,7 +160,7 @@ protected List sortAdvisors(List advisors) { *

The default implementation is empty. *

Typically used to add Advisors that expose contextual information * required by some of the later advisors. - * @param candidateAdvisors Advisors that have already been identified as + * @param candidateAdvisors the Advisors that have already been identified as * applying to a given bean */ protected void extendAdvisors(List candidateAdvisors) { 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 e19354723a0d..6e6aae64ba38 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-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. @@ -16,7 +16,6 @@ package org.springframework.aop.framework.autoproxy; -import java.beans.PropertyDescriptor; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; @@ -109,10 +108,10 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport protected static final Object[] PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS = new Object[0]; - /** Logger available to subclasses */ + /** Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); - /** Default is global AdvisorAdapterRegistry */ + /** Default is global AdvisorAdapterRegistry. */ private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance(); /** @@ -121,7 +120,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport */ private boolean freezeProxy = false; - /** Default is no common interceptors */ + /** Default is no common interceptors. */ private String[] interceptorNames = new String[0]; private boolean applyCommonInterceptorsFirst = true; @@ -134,7 +133,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport private final Set targetSourcedBeans = Collections.newSetFromMap(new ConcurrentHashMap<>(16)); - private final Set earlyProxyReferences = Collections.newSetFromMap(new ConcurrentHashMap<>(16)); + private final Map earlyProxyReferences = new ConcurrentHashMap<>(16); private final Map> proxyTypes = new ConcurrentHashMap<>(16); @@ -230,21 +229,19 @@ public Class predictBeanType(Class beanClass, String beanName) { @Override @Nullable - public Constructor[] determineCandidateConstructors(Class beanClass, String beanName) throws BeansException { + public Constructor[] determineCandidateConstructors(Class beanClass, String beanName) { return null; } @Override - public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { + public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); - if (!this.earlyProxyReferences.contains(cacheKey)) { - this.earlyProxyReferences.add(cacheKey); - } + this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey); } @Override - public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { + public Object postProcessBeforeInstantiation(Class beanClass, String beanName) { Object cacheKey = getCacheKey(beanClass, beanName); if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) { @@ -280,9 +277,7 @@ public boolean postProcessAfterInstantiation(Object bean, String beanName) { } @Override - public PropertyValues postProcessPropertyValues( - PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) { - + public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { return pvs; } @@ -297,10 +292,10 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) { * @see #getAdvicesAndAdvisorsForBean */ @Override - public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException { + public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); - if (!this.earlyProxyReferences.contains(cacheKey)) { + if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } @@ -388,14 +383,17 @@ protected boolean isInfrastructureClass(Class beanClass) { /** * Subclasses should override this method to return {@code true} if the * given bean should not be considered for auto-proxying by this post-processor. - *

Sometimes we need to be able to avoid this happening if it will lead to - * a circular reference. This implementation returns {@code false}. + *

Sometimes we need to be able to avoid this happening, e.g. if it will lead to + * a circular reference or if the existing target instance needs to be preserved. + * This implementation returns {@code false} unless the bean name indicates an + * "original instance" according to {@code AutowireCapableBeanFactory} conventions. * @param beanClass the class of the bean * @param beanName the name of the bean * @return whether to skip the given bean + * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#ORIGINAL_INSTANCE_SUFFIX */ protected boolean shouldSkip(Class beanClass, String beanName) { - return false; + return AutoProxyUtils.isOriginalInstance(beanName, beanClass); } /** @@ -417,9 +415,9 @@ protected TargetSource getCustomTargetSource(Class beanClass, String beanName TargetSource ts = tsc.getTargetSource(beanClass, beanName); if (ts != null) { // Found a matching TargetSource. - if (logger.isDebugEnabled()) { - logger.debug("TargetSourceCreator [" + tsc + - " found custom TargetSource for bean with name '" + beanName + "'"); + if (logger.isTraceEnabled()) { + logger.trace("TargetSourceCreator [" + tsc + + "] found custom TargetSource for bean with name '" + beanName + "'"); } return ts; } @@ -525,10 +523,10 @@ protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] } } } - if (logger.isDebugEnabled()) { + if (logger.isTraceEnabled()) { int nrOfCommonInterceptors = commonInterceptors.length; int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0); - logger.debug("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors + + logger.trace("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors + " common interceptors and " + nrOfSpecificInterceptors + " specific interceptors"); } @@ -561,7 +559,7 @@ private Advisor[] resolveInterceptorNames() { * Subclasses may choose to implement this: for example, * to change the interfaces exposed. *

The default implementation is empty. - * @param proxyFactory ProxyFactory that is already configured with + * @param proxyFactory a ProxyFactory that is already configured with * TargetSource and interfaces and will be used to create the proxy * immediately after this method returns */ diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractBeanFactoryAwareAdvisingPostProcessor.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractBeanFactoryAwareAdvisingPostProcessor.java index c17cdc05eb80..b8a90d052aba 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractBeanFactoryAwareAdvisingPostProcessor.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractBeanFactoryAwareAdvisingPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,4 +64,10 @@ protected ProxyFactory prepareProxyFactory(Object bean, String beanName) { return proxyFactory; } + @Override + protected boolean isEligible(Object bean, String beanName) { + return (!AutoProxyUtils.isOriginalInstance(beanName, bean.getClass()) && + super.isEligible(bean, beanName)); + } + } diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AutoProxyUtils.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AutoProxyUtils.java index 2cf44715ceec..d90552b91057 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AutoProxyUtils.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AutoProxyUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,10 +16,12 @@ package org.springframework.aop.framework.autoproxy; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.core.Conventions; import org.springframework.lang.Nullable; +import org.springframework.util.StringUtils; /** * Utilities for auto-proxy aware components. @@ -63,7 +65,9 @@ public abstract class AutoProxyUtils { * @param beanName the name of the bean * @return whether the given bean should be proxied with its target class */ - public static boolean shouldProxyTargetClass(ConfigurableListableBeanFactory beanFactory, @Nullable String beanName) { + public static boolean shouldProxyTargetClass( + ConfigurableListableBeanFactory beanFactory, @Nullable String beanName) { + if (beanName != null && beanFactory.containsBeanDefinition(beanName)) { BeanDefinition bd = beanFactory.getBeanDefinition(beanName); return Boolean.TRUE.equals(bd.getAttribute(PRESERVE_TARGET_CLASS_ATTRIBUTE)); @@ -81,7 +85,9 @@ public static boolean shouldProxyTargetClass(ConfigurableListableBeanFactory bea * @see org.springframework.beans.factory.BeanFactory#getType(String) */ @Nullable - public static Class determineTargetClass(ConfigurableListableBeanFactory beanFactory, @Nullable String beanName) { + public static Class determineTargetClass( + ConfigurableListableBeanFactory beanFactory, @Nullable String beanName) { + if (beanName == null) { return null; } @@ -102,12 +108,30 @@ public static Class determineTargetClass(ConfigurableListableBeanFactory bean * @param targetClass the corresponding target class * @since 4.2.3 */ - static void exposeTargetClass(ConfigurableListableBeanFactory beanFactory, @Nullable String beanName, - Class targetClass) { + static void exposeTargetClass( + ConfigurableListableBeanFactory beanFactory, @Nullable String beanName, Class targetClass) { if (beanName != null && beanFactory.containsBeanDefinition(beanName)) { beanFactory.getMergedBeanDefinition(beanName).setAttribute(ORIGINAL_TARGET_CLASS_ATTRIBUTE, targetClass); } } + /** + * Determine whether the given bean name indicates an "original instance" + * according to {@link AutowireCapableBeanFactory#ORIGINAL_INSTANCE_SUFFIX}, + * skipping any proxy attempts for it. + * @param beanName the name of the bean + * @param beanClass the corresponding bean class + * @since 5.1 + * @see AutowireCapableBeanFactory#ORIGINAL_INSTANCE_SUFFIX + */ + static boolean isOriginalInstance(String beanName, Class beanClass) { + if (!StringUtils.hasLength(beanName) || beanName.length() != + beanClass.getName().length() + AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX.length()) { + return false; + } + return (beanName.startsWith(beanClass.getName()) && + beanName.endsWith(AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX)); + } + } diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanFactoryAdvisorRetrievalHelper.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanFactoryAdvisorRetrievalHelper.java index d69c6b2ff52a..291fa91083bd 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanFactoryAdvisorRetrievalHelper.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanFactoryAdvisorRetrievalHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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.autoproxy; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import org.apache.commons.logging.Log; @@ -45,7 +45,7 @@ public class BeanFactoryAdvisorRetrievalHelper { private final ConfigurableListableBeanFactory beanFactory; @Nullable - private String[] cachedAdvisorBeanNames; + private volatile String[] cachedAdvisorBeanNames; /** @@ -66,27 +66,24 @@ public BeanFactoryAdvisorRetrievalHelper(ConfigurableListableBeanFactory beanFac */ public List findAdvisorBeans() { // Determine list of advisor bean names, if not cached already. - String[] advisorNames = null; - synchronized (this) { - advisorNames = this.cachedAdvisorBeanNames; - if (advisorNames == null) { - // Do not initialize FactoryBeans here: We need to leave all regular beans - // uninitialized to let the auto-proxy creator apply to them! - advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( - this.beanFactory, Advisor.class, true, false); - this.cachedAdvisorBeanNames = advisorNames; - } + String[] advisorNames = this.cachedAdvisorBeanNames; + if (advisorNames == null) { + // Do not initialize FactoryBeans here: We need to leave all regular beans + // uninitialized to let the auto-proxy creator apply to them! + advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( + this.beanFactory, Advisor.class, true, false); + this.cachedAdvisorBeanNames = advisorNames; } if (advisorNames.length == 0) { - return new LinkedList<>(); + return new ArrayList<>(); } - List advisors = new LinkedList<>(); + List advisors = new ArrayList<>(); for (String name : advisorNames) { if (isEligibleBean(name)) { if (this.beanFactory.isCurrentlyInCreation(name)) { - if (logger.isDebugEnabled()) { - logger.debug("Skipping currently created advisor '" + name + "'"); + if (logger.isTraceEnabled()) { + logger.trace("Skipping currently created advisor '" + name + "'"); } } else { @@ -99,8 +96,8 @@ public List findAdvisorBeans() { BeanCreationException bce = (BeanCreationException) rootCause; String bceBeanName = bce.getBeanName(); if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) { - if (logger.isDebugEnabled()) { - logger.debug("Skipping advisor '" + name + + if (logger.isTraceEnabled()) { + logger.trace("Skipping advisor '" + name + "' with dependency on currently created bean: " + ex.getMessage()); } // Ignore: indicates a reference back to the bean we're trying to advise. diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java index 24b8c48475e9..d41a348ac611 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,7 +38,7 @@ @SuppressWarnings("serial") public class DefaultAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator implements BeanNameAware { - /** Separator between prefix and remainder of bean name */ + /** Separator between prefix and remainder of bean name. */ public static final String SEPARATOR = "."; diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/ProxyCreationContext.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/ProxyCreationContext.java index cc7f6c3275bc..d2345e9b2822 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/ProxyCreationContext.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/ProxyCreationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author 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,13 +27,17 @@ * @author Ramnivas Laddad * @since 2.5 */ -public class ProxyCreationContext { +public final class ProxyCreationContext { - /** ThreadLocal holding the current proxied bean name during Advisor matching */ + /** ThreadLocal holding the current proxied bean name during Advisor matching. */ private static final ThreadLocal currentProxiedBeanName = new NamedThreadLocal<>("Name of currently proxied bean"); + private ProxyCreationContext() { + } + + /** * Return the name of the currently proxied bean instance. * @return the name of the bean, or {@code null} if none available diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/package-info.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/package-info.java index fde3c07636a2..328312146ade 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/package-info.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/package-info.java @@ -14,4 +14,4 @@ package org.springframework.aop.framework.autoproxy; import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; \ No newline at end of file +import org.springframework.lang.NonNullFields; diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/AbstractBeanFactoryBasedTargetSourceCreator.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/AbstractBeanFactoryBasedTargetSourceCreator.java index 03ec6dd1271a..d161fa2ddf69 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/AbstractBeanFactoryBasedTargetSourceCreator.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/AbstractBeanFactoryBasedTargetSourceCreator.java @@ -60,7 +60,7 @@ public abstract class AbstractBeanFactoryBasedTargetSourceCreator private ConfigurableBeanFactory beanFactory; - /** Internally used DefaultListableBeanFactory instances, keyed by bean name */ + /** Internally used DefaultListableBeanFactory instances, keyed by bean name. */ private final Map internalBeanFactories = new HashMap<>(); diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/QuickTargetSourceCreator.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/QuickTargetSourceCreator.java index 6101658fa459..8ddcba030d9f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/QuickTargetSourceCreator.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/QuickTargetSourceCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2018 the original author 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,8 +37,19 @@ */ public class QuickTargetSourceCreator extends AbstractBeanFactoryBasedTargetSourceCreator { + /** + * The CommonsPool2TargetSource prefix. + */ public static final String PREFIX_COMMONS_POOL = ":"; + + /** + * The ThreadLocalTargetSource prefix. + */ public static final String PREFIX_THREAD_LOCAL = "%"; + + /** + * The PrototypeTargetSource prefix. + */ public static final String PREFIX_PROTOTYPE = "!"; @Override diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/package-info.java b/spring-aop/src/main/java/org/springframework/aop/framework/package-info.java index 1e92cc203374..c05af5dea98a 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/package-info.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/package-info.java @@ -17,4 +17,4 @@ package org.springframework.aop.framework; import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; \ No newline at end of file +import org.springframework.lang.NonNullFields; 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 ce6c7f264287..9c68df587f18 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 @@ -124,6 +124,7 @@ public void setLogExceptionStackTrace(boolean logExceptionStackTrace) { * @see #invokeUnderTrace(org.aopalliance.intercept.MethodInvocation, org.apache.commons.logging.Log) */ @Override + @Nullable public Object invoke(MethodInvocation invocation) throws Throwable { Log logger = getLoggerForInvocation(invocation); if (isInterceptorEnabled(invocation, logger)) { @@ -242,6 +243,7 @@ protected void writeToLog(Log logger, String message, @Nullable Throwable ex) { * @see #writeToLog(Log, String) * @see #writeToLog(Log, String, Throwable) */ + @Nullable protected abstract Object invokeUnderTrace(MethodInvocation invocation, Log logger) throws Throwable; } diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java index 7d49865efc06..7eef59f1b8a9 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.Future; +import java.util.function.Supplier; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -41,6 +42,7 @@ import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; import org.springframework.util.concurrent.ListenableFuture; +import org.springframework.util.function.SingletonSupplier; /** * Base class for asynchronous method execution aspects, such as @@ -72,10 +74,9 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { private final Map executors = new ConcurrentHashMap<>(16); - @Nullable - private volatile Executor defaultExecutor; + private SingletonSupplier defaultExecutor; - private AsyncUncaughtExceptionHandler exceptionHandler; + private SingletonSupplier exceptionHandler; @Nullable private BeanFactory beanFactory; @@ -89,7 +90,8 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { * executor will be looked up at invocation time against the enclosing bean factory */ public AsyncExecutionAspectSupport(@Nullable Executor defaultExecutor) { - this(defaultExecutor, new SimpleAsyncUncaughtExceptionHandler()); + this.defaultExecutor = new SingletonSupplier<>(defaultExecutor, () -> getDefaultExecutor(this.beanFactory)); + this.exceptionHandler = SingletonSupplier.of(SimpleAsyncUncaughtExceptionHandler::new); } /** @@ -101,11 +103,23 @@ public AsyncExecutionAspectSupport(@Nullable Executor defaultExecutor) { * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use */ public AsyncExecutionAspectSupport(@Nullable Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) { - this.defaultExecutor = defaultExecutor; - this.exceptionHandler = exceptionHandler; + this.defaultExecutor = new SingletonSupplier<>(defaultExecutor, () -> getDefaultExecutor(this.beanFactory)); + this.exceptionHandler = SingletonSupplier.of(exceptionHandler); } + /** + * Configure this aspect with the given executor and exception handler suppliers, + * applying the corresponding default if a supplier is not resolvable. + * @since 5.1 + */ + public void configure(@Nullable Supplier defaultExecutor, + @Nullable Supplier exceptionHandler) { + + this.defaultExecutor = new SingletonSupplier<>(defaultExecutor, () -> getDefaultExecutor(this.beanFactory)); + this.exceptionHandler = new SingletonSupplier<>(exceptionHandler, SimpleAsyncUncaughtExceptionHandler::new); + } + /** * Supply the executor to be used when executing async methods. * @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor} @@ -117,7 +131,7 @@ public AsyncExecutionAspectSupport(@Nullable Executor defaultExecutor, AsyncUnca * @see #getDefaultExecutor(BeanFactory) */ public void setExecutor(Executor defaultExecutor) { - this.defaultExecutor = defaultExecutor; + this.defaultExecutor = SingletonSupplier.of(defaultExecutor); } /** @@ -125,7 +139,7 @@ public void setExecutor(Executor defaultExecutor) { * thrown by invoking asynchronous methods with a {@code void} return type. */ public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) { - this.exceptionHandler = exceptionHandler; + this.exceptionHandler = SingletonSupplier.of(exceptionHandler); } /** @@ -155,15 +169,7 @@ protected AsyncTaskExecutor determineAsyncExecutor(Method method) { targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier); } else { - targetExecutor = this.defaultExecutor; - if (targetExecutor == null) { - synchronized (this.executors) { - if (this.defaultExecutor == null) { - this.defaultExecutor = getDefaultExecutor(this.beanFactory); - } - targetExecutor = this.defaultExecutor; - } - } + targetExecutor = this.defaultExecutor.get(); } if (targetExecutor == null) { return null; @@ -305,10 +311,10 @@ protected void handleError(Throwable ex, Method method, Object... params) throws else { // Could not transmit the exception to the caller with default executor try { - this.exceptionHandler.handleUncaughtException(ex, method, params); + this.exceptionHandler.obtain().handleUncaughtException(ex, method, params); } catch (Throwable ex2) { - logger.error("Exception handler for async method '" + method.toGenericString() + + logger.warn("Exception handler for async method '" + method.toGenericString() + "' threw unexpected exception itself", ex2); } } diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java index d69442e47431..c458723954ff 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncUncaughtExceptionHandler.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncUncaughtExceptionHandler.java index 2914e53ef5dc..ade30ae3af3f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncUncaughtExceptionHandler.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncUncaughtExceptionHandler.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. @@ -23,7 +23,7 @@ * *

An asynchronous method usually returns a {@link java.util.concurrent.Future} * instance that gives access to the underlying exception. When the method does - * not provide that return type, this handler can be used to managed such + * not provide that return type, this handler can be used to manage such * uncaught exceptions. * * @author Stephane Nicoll 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 b6318c2e4b2b..3a28e2140c86 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 @@ -79,7 +79,7 @@ public class CustomizableTraceInterceptor extends AbstractTraceInterceptor { /** * The {@code $[targetClassName]} placeholder. - * Replaced with the fully-qualifed name of the {@code Class} + * Replaced with the fully-qualified name of the {@code Class} * of the method invocation target. */ public static final String PLACEHOLDER_TARGET_CLASS_NAME = "$[targetClassName]"; 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 78479765ca6c..e12a20d0ff66 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-2012 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -62,7 +62,7 @@ public static String getBeanName() throws IllegalStateException { /** * Find the bean name for the given invocation. Assumes that an ExposeBeanNameAdvisor * has been included in the interceptor chain. - * @param mi MethodInvocation that should contain the bean name as an attribute + * @param mi the MethodInvocation that should contain the bean name as an attribute * @return the bean name (never {@code null}) * @throws IllegalStateException if the bean name has not been exposed */ @@ -80,7 +80,7 @@ public static String getBeanName(MethodInvocation mi) throws IllegalStateExcepti /** * Create a new advisor that will expose the given bean name, - * with no introduction + * with no introduction. * @param beanName bean name to expose */ public static Advisor createAdvisorWithoutIntroduction(String beanName) { 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 d4368f0ee87a..8770607af2d8 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-2016 the original author or authors. + * Copyright 2002-2018 the original author 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,9 +41,9 @@ * @author Juergen Hoeller */ @SuppressWarnings("serial") -public class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable { +public final class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable { - /** Singleton instance of this class */ + /** Singleton instance of this class. */ public static final ExposeInvocationInterceptor INSTANCE = new ExposeInvocationInterceptor(); /** @@ -69,11 +69,12 @@ public String toString() { */ public static MethodInvocation currentInvocation() throws IllegalStateException { MethodInvocation mi = invocation.get(); - if (mi == null) + if (mi == null) { throw new IllegalStateException( "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!"); + } return mi; } diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleAsyncUncaughtExceptionHandler.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleAsyncUncaughtExceptionHandler.java index dffba79e80e8..0197d0ddd614 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleAsyncUncaughtExceptionHandler.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleAsyncUncaughtExceptionHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2018 the original author 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,18 @@ * A default {@link AsyncUncaughtExceptionHandler} that simply logs the exception. * * @author Stephane Nicoll + * @author Juergen Hoeller * @since 4.1 */ public class SimpleAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler { - private final Log logger = LogFactory.getLog(SimpleAsyncUncaughtExceptionHandler.class); + private static final Log logger = LogFactory.getLog(SimpleAsyncUncaughtExceptionHandler.class); + @Override public void handleUncaughtException(Throwable ex, Method method, Object... params) { if (logger.isErrorEnabled()) { - logger.error(String.format("Unexpected error occurred invoking async " + - "method '%s'.", method), ex); + logger.error("Unexpected exception occurred invoking async method: " + method, ex); } } diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/package-info.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/package-info.java index bc4bd2843710..eb2a05f4be05 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/package-info.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/package-info.java @@ -8,4 +8,4 @@ package org.springframework.aop.interceptor; import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; \ No newline at end of file +import org.springframework.lang.NonNullFields; diff --git a/spring-aop/src/main/java/org/springframework/aop/package-info.java b/spring-aop/src/main/java/org/springframework/aop/package-info.java index c2e18abd3b30..2b87bce534c7 100644 --- a/spring-aop/src/main/java/org/springframework/aop/package-info.java +++ b/spring-aop/src/main/java/org/springframework/aop/package-info.java @@ -22,4 +22,4 @@ package org.springframework.aop; import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; \ No newline at end of file +import org.springframework.lang.NonNullFields; diff --git a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyFactoryBean.java b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyFactoryBean.java index 8889e117a79a..c2e6f14bb4ee 100644 --- a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyFactoryBean.java +++ b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,16 +52,17 @@ * @see #setProxyTargetClass */ @SuppressWarnings("serial") -public class ScopedProxyFactoryBean extends ProxyConfig implements FactoryBean, BeanFactoryAware { +public class ScopedProxyFactoryBean extends ProxyConfig + implements FactoryBean, BeanFactoryAware, AopInfrastructureBean { - /** The TargetSource that manages scoping */ + /** The TargetSource that manages scoping. */ private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource(); - /** The name of the target bean */ + /** The name of the target bean. */ @Nullable private String targetBeanName; - /** The cached singleton proxy */ + /** The cached singleton proxy. */ @Nullable private Object proxy; diff --git a/spring-aop/src/main/java/org/springframework/aop/scope/package-info.java b/spring-aop/src/main/java/org/springframework/aop/scope/package-info.java index 10bb1730aa13..443f903968fb 100644 --- a/spring-aop/src/main/java/org/springframework/aop/scope/package-info.java +++ b/spring-aop/src/main/java/org/springframework/aop/scope/package-info.java @@ -6,4 +6,4 @@ package org.springframework.aop.scope; import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; \ No newline at end of file +import org.springframework.lang.NonNullFields; diff --git a/spring-aop/src/main/java/org/springframework/aop/support/AbstractRegexpMethodPointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/AbstractRegexpMethodPointcut.java index f6c715279626..d96f65f6e0ac 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/AbstractRegexpMethodPointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/AbstractRegexpMethodPointcut.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author 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,6 @@ import java.lang.reflect.Method; import java.util.Arrays; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -130,9 +129,10 @@ public String[] getExcludedPatterns() { * plus the name of the method. */ @Override - public boolean matches(Method method, @Nullable Class targetClass) { - return ((targetClass != null && matchesPattern(ClassUtils.getQualifiedMethodName(method, targetClass))) || - matchesPattern(ClassUtils.getQualifiedMethodName(method))); + public boolean matches(Method method, Class targetClass) { + return (matchesPattern(ClassUtils.getQualifiedMethodName(method, targetClass)) || + (targetClass != method.getDeclaringClass() && + matchesPattern(ClassUtils.getQualifiedMethodName(method, method.getDeclaringClass())))); } /** diff --git a/spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java b/spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java index d7e3665f1b68..83358da1594b 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java @@ -20,8 +20,8 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; +import java.util.ArrayList; import java.util.LinkedHashSet; -import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -305,7 +305,7 @@ public static List findAdvisorsThatCanApply(List candidateAdvi if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } - List eligibleAdvisors = new LinkedList<>(); + List eligibleAdvisors = new ArrayList<>(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); diff --git a/spring-aop/src/main/java/org/springframework/aop/support/ComposablePointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/ComposablePointcut.java index 50a67d2ae2f6..501d8194b653 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/ComposablePointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/ComposablePointcut.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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 @@ */ public class ComposablePointcut implements Pointcut, Serializable { - /** use serialVersionUID from Spring 1.2 for interoperability */ + /** use serialVersionUID from Spring 1.2 for interoperability. */ private static final long serialVersionUID = -2743223737633663832L; private ClassFilter classFilter; 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 1f2e65f113cd..b8545095c7a1 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-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. @@ -18,6 +18,7 @@ import java.io.Serializable; import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicInteger; import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; @@ -43,7 +44,7 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher @Nullable private String methodName; - private volatile int evaluations; + private final AtomicInteger evaluations = new AtomicInteger(0); /** @@ -77,11 +78,10 @@ public boolean matches(Class clazz) { } /** - * Subclasses can override this if it's possible to filter out - * some candidate classes. + * Subclasses can override this if it's possible to filter out some candidate classes. */ @Override - public boolean matches(Method method, @Nullable Class targetClass) { + public boolean matches(Method method, Class targetClass) { return true; } @@ -91,8 +91,8 @@ public boolean isRuntime() { } @Override - public boolean matches(Method method, @Nullable Class targetClass, Object... args) { - this.evaluations++; + public boolean matches(Method method, Class targetClass, Object... args) { + this.evaluations.incrementAndGet(); for (StackTraceElement element : new Throwable().getStackTrace()) { if (element.getClassName().equals(this.clazz.getName()) && @@ -107,7 +107,7 @@ public boolean matches(Method method, @Nullable Class targetClass, Object... * It's useful to know how many times we've fired, for optimization. */ public int getEvaluations() { - return this.evaluations; + return this.evaluations.get(); } @@ -131,7 +131,7 @@ public boolean equals(Object other) { return false; } ControlFlowPointcut that = (ControlFlowPointcut) other; - return (this.clazz.equals(that.clazz)) && ObjectUtils.nullSafeEquals(that.methodName, this.methodName); + return (this.clazz.equals(that.clazz)) && ObjectUtils.nullSafeEquals(this.methodName, that.methodName); } @Override diff --git a/spring-aop/src/main/java/org/springframework/aop/support/DefaultIntroductionAdvisor.java b/spring-aop/src/main/java/org/springframework/aop/support/DefaultIntroductionAdvisor.java index b6ad61c4825b..d675d9dc430f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/DefaultIntroductionAdvisor.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/DefaultIntroductionAdvisor.java @@ -113,8 +113,8 @@ public void validateInterfaces() throws IllegalArgumentException { for (Class ifc : this.interfaces) { if (this.advice instanceof DynamicIntroductionAdvice && !((DynamicIntroductionAdvice) this.advice).implementsInterface(ifc)) { - throw new IllegalArgumentException("DynamicIntroductionAdvice [" + this.advice + "] " + - "does not implement interface [" + ifc.getName() + "] specified for introduction"); + throw new IllegalArgumentException("DynamicIntroductionAdvice [" + this.advice + "] " + + "does not implement interface [" + ifc.getName() + "] specified for introduction"); } } } diff --git a/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcher.java b/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcher.java index b46b0e4912f3..1f7dc9748415 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcher.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author 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,12 @@ import java.lang.reflect.Method; import org.springframework.aop.MethodMatcher; -import org.springframework.lang.Nullable; /** * Convenient abstract superclass for dynamic method matchers, * which do care about arguments at runtime. + * + * @author Rod Johnson */ public abstract class DynamicMethodMatcher implements MethodMatcher { @@ -37,7 +38,7 @@ public final boolean isRuntime() { * always returns true. */ @Override - public boolean matches(Method method, @Nullable Class targetClass) { + public boolean matches(Method method, Class targetClass) { return true; } diff --git a/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcherPointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcherPointcut.java index df3963dc8530..56e29cb0bd94 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcherPointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcherPointcut.java @@ -24,7 +24,7 @@ * Convenient superclass when we want to force subclasses to * implement MethodMatcher interface, but subclasses * will want to be pointcuts. The getClassFilter() method can - * be overriden to customize ClassFilter behaviour as well. + * be overridden to customize ClassFilter behaviour as well. * * @author Rod Johnson */ diff --git a/spring-aop/src/main/java/org/springframework/aop/support/MethodMatchers.java b/spring-aop/src/main/java/org/springframework/aop/support/MethodMatchers.java index 30de25c2c1e8..2d3d0b678ddb 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/MethodMatchers.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/MethodMatchers.java @@ -22,7 +22,6 @@ import org.springframework.aop.ClassFilter; import org.springframework.aop.IntroductionAwareMethodMatcher; import org.springframework.aop.MethodMatcher; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -49,7 +48,8 @@ public abstract class MethodMatchers { * of the given MethodMatchers matches */ public static MethodMatcher union(MethodMatcher mm1, MethodMatcher mm2) { - return new UnionMethodMatcher(mm1, mm2); + return (mm1 instanceof IntroductionAwareMethodMatcher || mm2 instanceof IntroductionAwareMethodMatcher ? + new UnionIntroductionAwareMethodMatcher(mm1, mm2) : new UnionMethodMatcher(mm1, mm2)); } /** @@ -62,7 +62,9 @@ public static MethodMatcher union(MethodMatcher mm1, MethodMatcher mm2) { * of the given MethodMatchers matches */ static MethodMatcher union(MethodMatcher mm1, ClassFilter cf1, MethodMatcher mm2, ClassFilter cf2) { - return new ClassFilterAwareUnionMethodMatcher(mm1, cf1, mm2, cf2); + return (mm1 instanceof IntroductionAwareMethodMatcher || mm2 instanceof IntroductionAwareMethodMatcher ? + new ClassFilterAwareUnionIntroductionAwareMethodMatcher(mm1, cf1, mm2, cf2) : + new ClassFilterAwareUnionMethodMatcher(mm1, cf1, mm2, cf2)); } /** @@ -73,7 +75,8 @@ static MethodMatcher union(MethodMatcher mm1, ClassFilter cf1, MethodMatcher mm2 * of the given MethodMatchers match */ public static MethodMatcher intersection(MethodMatcher mm1, MethodMatcher mm2) { - return new IntersectionMethodMatcher(mm1, mm2); + return (mm1 instanceof IntroductionAwareMethodMatcher || mm2 instanceof IntroductionAwareMethodMatcher ? + new IntersectionIntroductionAwareMethodMatcher(mm1, mm2) : new IntersectionMethodMatcher(mm1, mm2)); } /** @@ -82,13 +85,12 @@ public static MethodMatcher intersection(MethodMatcher mm1, MethodMatcher mm2) { * (if applicable). * @param mm the MethodMatcher to apply (may be an IntroductionAwareMethodMatcher) * @param method the candidate method - * @param targetClass the target class (may be {@code null}, in which case - * the candidate class must be taken to be the method's declaring class) + * @param targetClass the target class * @param hasIntroductions {@code true} if the object on whose behalf we are * asking is the subject on one or more introductions; {@code false} otherwise * @return whether or not this method matches statically */ - public static boolean matches(MethodMatcher mm, Method method, @Nullable Class targetClass, boolean hasIntroductions) { + public static boolean matches(MethodMatcher mm, Method method, Class targetClass, boolean hasIntroductions) { Assert.notNull(mm, "MethodMatcher must not be null"); return (mm instanceof IntroductionAwareMethodMatcher ? ((IntroductionAwareMethodMatcher) mm).matches(method, targetClass, hasIntroductions) : @@ -100,11 +102,11 @@ public static boolean matches(MethodMatcher mm, Method method, @Nullable Class targetClass, boolean hasIntroductions) { - return (matchesClass1(targetClass) && MethodMatchers.matches(this.mm1, method, targetClass, hasIntroductions)) || - (matchesClass2(targetClass) && MethodMatchers.matches(this.mm2, method, targetClass, hasIntroductions)); - } - - @Override - public boolean matches(Method method, @Nullable Class targetClass) { + public boolean matches(Method method, Class targetClass) { return (matchesClass1(targetClass) && this.mm1.matches(method, targetClass)) || (matchesClass2(targetClass) && this.mm2.matches(method, targetClass)); } - protected boolean matchesClass1(@Nullable Class targetClass) { + protected boolean matchesClass1(Class targetClass) { return true; } - protected boolean matchesClass2(@Nullable Class targetClass) { + protected boolean matchesClass2(Class targetClass) { return true; } @@ -139,28 +135,46 @@ public boolean isRuntime() { } @Override - public boolean matches(Method method, @Nullable Class targetClass, Object... args) { + public boolean matches(Method method, Class targetClass, Object... args) { return this.mm1.matches(method, targetClass, args) || this.mm2.matches(method, targetClass, args); } @Override - public boolean equals(Object obj) { - if (this == obj) { + public boolean equals(Object other) { + if (this == other) { return true; } - if (!(obj instanceof UnionMethodMatcher)) { + if (!(other instanceof UnionMethodMatcher)) { return false; } - UnionMethodMatcher that = (UnionMethodMatcher) obj; + UnionMethodMatcher that = (UnionMethodMatcher) other; return (this.mm1.equals(that.mm1) && this.mm2.equals(that.mm2)); } @Override public int hashCode() { - int hashCode = 17; - hashCode = 37 * hashCode + this.mm1.hashCode(); - hashCode = 37 * hashCode + this.mm2.hashCode(); - return hashCode; + return 37 * this.mm1.hashCode() + this.mm2.hashCode(); + } + } + + + /** + * MethodMatcher implementation for a union of two given MethodMatchers + * of which at least one is an IntroductionAwareMethodMatcher. + * @since 5.1 + */ + @SuppressWarnings("serial") + private static class UnionIntroductionAwareMethodMatcher extends UnionMethodMatcher + implements IntroductionAwareMethodMatcher { + + public UnionIntroductionAwareMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) { + super(mm1, mm2); + } + + @Override + public boolean matches(Method method, Class targetClass, boolean hasIntroductions) { + return (matchesClass1(targetClass) && MethodMatchers.matches(this.mm1, method, targetClass, hasIntroductions)) || + (matchesClass2(targetClass) && MethodMatchers.matches(this.mm2, method, targetClass, hasIntroductions)); } } @@ -183,13 +197,13 @@ public ClassFilterAwareUnionMethodMatcher(MethodMatcher mm1, ClassFilter cf1, Me } @Override - protected boolean matchesClass1(@Nullable Class targetClass) { - return (targetClass != null && this.cf1.matches(targetClass)); + protected boolean matchesClass1(Class targetClass) { + return this.cf1.matches(targetClass); } @Override - protected boolean matchesClass2(@Nullable Class targetClass) { - return (targetClass != null && this.cf2.matches(targetClass)); + protected boolean matchesClass2(Class targetClass) { + return this.cf2.matches(targetClass); } @Override @@ -209,6 +223,36 @@ public boolean equals(Object other) { } return (this.cf1.equals(otherCf1) && this.cf2.equals(otherCf2)); } + + @Override + public int hashCode() { + // Allow for matching with regular UnionMethodMatcher by providing same hash... + return super.hashCode(); + } + } + + + /** + * MethodMatcher implementation for a union of two given MethodMatchers + * of which at least one is an IntroductionAwareMethodMatcher, + * supporting an associated ClassFilter per MethodMatcher. + * @since 5.1 + */ + @SuppressWarnings("serial") + private static class ClassFilterAwareUnionIntroductionAwareMethodMatcher extends ClassFilterAwareUnionMethodMatcher + implements IntroductionAwareMethodMatcher { + + public ClassFilterAwareUnionIntroductionAwareMethodMatcher( + MethodMatcher mm1, ClassFilter cf1, MethodMatcher mm2, ClassFilter cf2) { + + super(mm1, cf1, mm2, cf2); + } + + @Override + public boolean matches(Method method, Class targetClass, boolean hasIntroductions) { + return (matchesClass1(targetClass) && MethodMatchers.matches(this.mm1, method, targetClass, hasIntroductions)) || + (matchesClass2(targetClass) && MethodMatchers.matches(this.mm2, method, targetClass, hasIntroductions)); + } } @@ -216,11 +260,11 @@ public boolean equals(Object other) { * MethodMatcher implementation for an intersection of two given MethodMatchers. */ @SuppressWarnings("serial") - private static class IntersectionMethodMatcher implements IntroductionAwareMethodMatcher, Serializable { + private static class IntersectionMethodMatcher implements MethodMatcher, Serializable { - private final MethodMatcher mm1; + protected final MethodMatcher mm1; - private final MethodMatcher mm2; + protected final MethodMatcher mm2; public IntersectionMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) { Assert.notNull(mm1, "First MethodMatcher must not be null"); @@ -230,30 +274,24 @@ public IntersectionMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) { } @Override - public boolean matches(Method method, @Nullable Class targetClass, boolean hasIntroductions) { - return MethodMatchers.matches(this.mm1, method, targetClass, hasIntroductions) && - MethodMatchers.matches(this.mm2, method, targetClass, hasIntroductions); - } - - @Override - public boolean matches(Method method, @Nullable Class targetClass) { - return this.mm1.matches(method, targetClass) && this.mm2.matches(method, targetClass); + public boolean matches(Method method, Class targetClass) { + return (this.mm1.matches(method, targetClass) && this.mm2.matches(method, targetClass)); } @Override public boolean isRuntime() { - return this.mm1.isRuntime() || this.mm2.isRuntime(); + return (this.mm1.isRuntime() || this.mm2.isRuntime()); } @Override - public boolean matches(Method method, @Nullable Class targetClass, Object... args) { + public boolean matches(Method method, Class targetClass, Object... args) { // Because a dynamic intersection may be composed of a static and dynamic part, // we must avoid calling the 3-arg matches method on a dynamic matcher, as // it will probably be an unsupported operation. - boolean aMatches = this.mm1.isRuntime() ? - this.mm1.matches(method, targetClass, args) : this.mm1.matches(method, targetClass); - boolean bMatches = this.mm2.isRuntime() ? - this.mm2.matches(method, targetClass, args) : this.mm2.matches(method, targetClass); + boolean aMatches = (this.mm1.isRuntime() ? + this.mm1.matches(method, targetClass, args) : this.mm1.matches(method, targetClass)); + boolean bMatches = (this.mm2.isRuntime() ? + this.mm2.matches(method, targetClass, args) : this.mm2.matches(method, targetClass)); return aMatches && bMatches; } @@ -271,10 +309,28 @@ public boolean equals(Object other) { @Override public int hashCode() { - int hashCode = 17; - hashCode = 37 * hashCode + this.mm1.hashCode(); - hashCode = 37 * hashCode + this.mm2.hashCode(); - return hashCode; + return 37 * this.mm1.hashCode() + this.mm2.hashCode(); + } + } + + + /** + * MethodMatcher implementation for an intersection of two given MethodMatchers + * of which at least one is an IntroductionAwareMethodMatcher. + * @since 5.1 + */ + @SuppressWarnings("serial") + private static class IntersectionIntroductionAwareMethodMatcher extends IntersectionMethodMatcher + implements IntroductionAwareMethodMatcher { + + public IntersectionIntroductionAwareMethodMatcher(MethodMatcher mm1, MethodMatcher mm2) { + super(mm1, mm2); + } + + @Override + public boolean matches(Method method, Class targetClass, boolean hasIntroductions) { + return (MethodMatchers.matches(this.mm1, method, targetClass, hasIntroductions) && + MethodMatchers.matches(this.mm2, method, targetClass, hasIntroductions)); } } 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 2b2b7d85ee69..19009b457927 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,11 +18,10 @@ import java.io.Serializable; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; -import java.util.LinkedList; import java.util.List; -import org.springframework.lang.Nullable; import org.springframework.util.PatternMatchUtils; /** @@ -38,7 +37,7 @@ @SuppressWarnings("serial") public class NameMatchMethodPointcut extends StaticMethodMatcherPointcut implements Serializable { - private List mappedNames = new LinkedList<>(); + private List mappedNames = new ArrayList<>(); /** @@ -55,11 +54,8 @@ public void setMappedName(String mappedName) { * Matching will be the union of all these; if any match, * the pointcut matches. */ - public void setMappedNames(@Nullable String... mappedNames) { - this.mappedNames = new LinkedList<>(); - if (mappedNames != null) { - this.mappedNames.addAll(Arrays.asList(mappedNames)); - } + public void setMappedNames(String... mappedNames) { + this.mappedNames = new ArrayList<>(Arrays.asList(mappedNames)); } /** @@ -78,7 +74,7 @@ public NameMatchMethodPointcut addMethodName(String name) { @Override - public boolean matches(Method method, @Nullable Class targetClass) { + public boolean matches(Method method, Class targetClass) { for (String mappedName : this.mappedNames) { if (mappedName.equals(method.getName()) || isMatch(method.getName(), mappedName)) { return true; diff --git a/spring-aop/src/main/java/org/springframework/aop/support/Pointcuts.java b/spring-aop/src/main/java/org/springframework/aop/support/Pointcuts.java index 42475d9bb829..d362b6fe8891 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/Pointcuts.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/Pointcuts.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2018 the original author 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 org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -35,10 +34,10 @@ */ public abstract class Pointcuts { - /** Pointcut matching all bean property setters, in any class */ + /** Pointcut matching all bean property setters, in any class. */ public static final Pointcut SETTERS = SetterPointcut.INSTANCE; - /** Pointcut matching all bean property getters, in any class */ + /** Pointcut matching all bean property getters, in any class. */ public static final Pointcut GETTERS = GetterPointcut.INSTANCE; @@ -98,7 +97,7 @@ private static class SetterPointcut extends StaticMethodMatcherPointcut implemen public static final SetterPointcut INSTANCE = new SetterPointcut(); @Override - public boolean matches(Method method, @Nullable Class targetClass) { + public boolean matches(Method method, Class targetClass) { return (method.getName().startsWith("set") && method.getParameterCount() == 1 && method.getReturnType() == Void.TYPE); @@ -119,7 +118,7 @@ private static class GetterPointcut extends StaticMethodMatcherPointcut implemen public static final GetterPointcut INSTANCE = new GetterPointcut(); @Override - public boolean matches(Method method, @Nullable Class targetClass) { + public boolean matches(Method method, Class targetClass) { return (method.getName().startsWith("get") && method.getParameterCount() == 0); } diff --git a/spring-aop/src/main/java/org/springframework/aop/support/RegexpMethodPointcutAdvisor.java b/spring-aop/src/main/java/org/springframework/aop/support/RegexpMethodPointcutAdvisor.java index 896963d36b2c..538d04786624 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/RegexpMethodPointcutAdvisor.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/RegexpMethodPointcutAdvisor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,7 @@ public Pointcut getPointcut() { this.pointcut.setPatterns(this.patterns); } } - return pointcut; + return this.pointcut; } } diff --git a/spring-aop/src/main/java/org/springframework/aop/support/RootClassFilter.java b/spring-aop/src/main/java/org/springframework/aop/support/RootClassFilter.java index 364ff6282ec1..6b8073d69a2a 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/RootClassFilter.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/RootClassFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author 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,8 @@ import org.springframework.aop.ClassFilter; /** - * Simple ClassFilter implementation that passes classes (and optionally subclasses) + * Simple ClassFilter implementation that passes classes (and optionally subclasses). + * * @author Rod Johnson */ @SuppressWarnings("serial") @@ -37,7 +38,7 @@ public RootClassFilter(Class clazz) { @Override public boolean matches(Class candidate) { - return clazz.isAssignableFrom(candidate); + return this.clazz.isAssignableFrom(candidate); } } diff --git a/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java b/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java index 76f89ab3eb44..923daaf94d06 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2018 the original author 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,12 @@ import java.lang.reflect.Method; import org.springframework.aop.MethodMatcher; -import org.springframework.lang.Nullable; /** * Convenient abstract superclass for static method matchers, which don't care * about arguments at runtime. + * + * @author Rod Johnson */ public abstract class StaticMethodMatcher implements MethodMatcher { @@ -33,7 +34,7 @@ public final boolean isRuntime() { } @Override - public final boolean matches(Method method, @Nullable Class targetClass, Object... args) { + public final boolean matches(Method method, Class targetClass, Object... args) { // should never be invoked because isRuntime() returns false throw new UnsupportedOperationException("Illegal MethodMatcher usage"); } diff --git a/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationClassFilter.java b/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationClassFilter.java index bb7f1967af9b..9c53d5b80cf5 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationClassFilter.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationClassFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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 java.lang.annotation.Annotation; import org.springframework.aop.ClassFilter; -import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.util.Assert; /** @@ -50,7 +50,7 @@ public AnnotationClassFilter(Class annotationType) { * @param annotationType the annotation type to look for * @param checkInherited whether to also check the superclasses and * interfaces as well as meta-annotations for the annotation type - * (i.e. whether to use {@link AnnotationUtils#findAnnotation(Class, Class)} + * (i.e. whether to use {@link AnnotatedElementUtils#hasAnnotation} * semantics instead of standard Java {@link Class#isAnnotationPresent}) */ public AnnotationClassFilter(Class annotationType, boolean checkInherited) { @@ -62,8 +62,7 @@ public AnnotationClassFilter(Class annotationType, boolean @Override public boolean matches(Class clazz) { - return (this.checkInherited ? - (AnnotationUtils.findAnnotation(clazz, this.annotationType) != null) : + return (this.checkInherited ? AnnotatedElementUtils.hasAnnotation(clazz, this.annotationType) : clazz.isAnnotationPresent(this.annotationType)); } diff --git a/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationMatchingPointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationMatchingPointcut.java index 2eb8b15bd5a5..4a7a06b24313 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationMatchingPointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationMatchingPointcut.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. @@ -21,6 +21,7 @@ import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -96,7 +97,7 @@ public AnnotationMatchingPointcut(@Nullable Class classAnn this.classFilter = new AnnotationClassFilter(classAnnotationType, checkInherited); } else { - this.classFilter = ClassFilter.TRUE; + this.classFilter = new AnnotationCandidateClassFilter(methodAnnotationType); } if (methodAnnotationType != null) { @@ -164,4 +165,23 @@ public static AnnotationMatchingPointcut forMethodAnnotation(Class annotationType; + + public AnnotationCandidateClassFilter(Class annotationType) { + this.annotationType = annotationType; + } + + @Override + public boolean matches(Class clazz) { + return AnnotationUtils.isCandidateClass(clazz, this.annotationType); + } + } + } diff --git a/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationMethodMatcher.java b/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationMethodMatcher.java index 63caee265e7e..263f1391aa6c 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationMethodMatcher.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationMethodMatcher.java @@ -22,8 +22,7 @@ import org.springframework.aop.support.AopUtils; import org.springframework.aop.support.StaticMethodMatcher; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.lang.Nullable; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.util.Assert; /** @@ -55,7 +54,7 @@ public AnnotationMethodMatcher(Class annotationType) { * @param annotationType the annotation type to look for * @param checkInherited whether to also check the superclasses and * interfaces as well as meta-annotations for the annotation type - * (i.e. whether to use {@link AnnotationUtils#findAnnotation(Method, Class)} + * (i.e. whether to use {@link AnnotatedElementUtils#hasAnnotation} * semantics instead of standard Java {@link Method#isAnnotationPresent}) * @since 5.0 */ @@ -68,12 +67,12 @@ public AnnotationMethodMatcher(Class annotationType, boole @Override - public boolean matches(Method method, @Nullable Class targetClass) { + public boolean matches(Method method, Class targetClass) { if (matchesMethod(method)) { return true; } // Proxy classes never have annotations on their redeclared methods. - if (targetClass != null && Proxy.isProxyClass(targetClass)) { + if (Proxy.isProxyClass(targetClass)) { return false; } // The method may be on an interface, so let's check on the target class as well. @@ -82,8 +81,7 @@ public boolean matches(Method method, @Nullable Class targetClass) { } private boolean matchesMethod(Method method) { - return (this.checkInherited ? - (AnnotationUtils.findAnnotation(method, this.annotationType) != null) : + return (this.checkInherited ? AnnotatedElementUtils.hasAnnotation(method, this.annotationType) : method.isAnnotationPresent(this.annotationType)); } diff --git a/spring-aop/src/main/java/org/springframework/aop/support/annotation/package-info.java b/spring-aop/src/main/java/org/springframework/aop/support/annotation/package-info.java index 9abf44f9cf7b..a5ec1d421ab5 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/annotation/package-info.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/annotation/package-info.java @@ -6,4 +6,4 @@ package org.springframework.aop.support.annotation; import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; \ No newline at end of file +import org.springframework.lang.NonNullFields; diff --git a/spring-aop/src/main/java/org/springframework/aop/support/package-info.java b/spring-aop/src/main/java/org/springframework/aop/support/package-info.java index 74ec5f9b9dfe..a39f2d4c302c 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/package-info.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/package-info.java @@ -6,4 +6,4 @@ package org.springframework.aop.support; import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; \ No newline at end of file +import org.springframework.lang.NonNullFields; diff --git a/spring-aop/src/main/java/org/springframework/aop/target/AbstractBeanFactoryBasedTargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/AbstractBeanFactoryBasedTargetSource.java index b070b628eada..554222274065 100644 --- a/spring-aop/src/main/java/org/springframework/aop/target/AbstractBeanFactoryBasedTargetSource.java +++ b/spring-aop/src/main/java/org/springframework/aop/target/AbstractBeanFactoryBasedTargetSource.java @@ -49,17 +49,17 @@ */ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSource, BeanFactoryAware, Serializable { - /** use serialVersionUID from Spring 1.2.7 for interoperability */ + /** use serialVersionUID from Spring 1.2.7 for interoperability. */ private static final long serialVersionUID = -4721607536018568393L; - /** Logger available to subclasses */ + /** Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); - /** Name of the target bean we will create on each invocation */ + /** Name of the target bean we will create on each invocation. */ private String targetBeanName; - /** Class of the target */ + /** Class of the target. */ private volatile Class targetClass; /** diff --git a/spring-aop/src/main/java/org/springframework/aop/target/AbstractLazyCreationTargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/AbstractLazyCreationTargetSource.java index 4c23c54c365f..81dbad3e3d40 100644 --- a/spring-aop/src/main/java/org/springframework/aop/target/AbstractLazyCreationTargetSource.java +++ b/spring-aop/src/main/java/org/springframework/aop/target/AbstractLazyCreationTargetSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,10 +42,10 @@ */ public abstract class AbstractLazyCreationTargetSource implements TargetSource { - /** Logger available to subclasses */ + /** Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); - /** The lazily initialized target object */ + /** The lazily initialized target object. */ private Object lazyTarget; 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 bfd4a5f147dc..ccdb5c7bb266 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-2015 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,7 +55,7 @@ public abstract class AbstractPoolingTargetSource extends AbstractPrototypeBasedTargetSource implements PoolingConfig, DisposableBean { - /** The maximum size of the pool */ + /** The maximum size of the pool. */ private int maxSize = -1; diff --git a/spring-aop/src/main/java/org/springframework/aop/target/AbstractPrototypeBasedTargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/AbstractPrototypeBasedTargetSource.java index 4376dcddf6f4..fe3d0614a50c 100644 --- a/spring-aop/src/main/java/org/springframework/aop/target/AbstractPrototypeBasedTargetSource.java +++ b/spring-aop/src/main/java/org/springframework/aop/target/AbstractPrototypeBasedTargetSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -74,8 +74,8 @@ protected Object newPrototypeInstance() throws BeansException { * @param target the bean instance to destroy */ protected void destroyPrototypeInstance(Object target) { - if (this.logger.isDebugEnabled()) { - this.logger.debug("Destroying instance of bean '" + getTargetBeanName() + "'"); + if (logger.isDebugEnabled()) { + logger.debug("Destroying instance of bean '" + getTargetBeanName() + "'"); } if (getBeanFactory() instanceof ConfigurableBeanFactory) { ((ConfigurableBeanFactory) getBeanFactory()).destroyBean(getTargetBeanName(), target); @@ -85,7 +85,7 @@ else if (target instanceof DisposableBean) { ((DisposableBean) target).destroy(); } catch (Throwable ex) { - logger.error("Couldn't invoke destroy method of bean with name '" + getTargetBeanName() + "'", ex); + logger.warn("Destroy method on bean with name '" + getTargetBeanName() + "' threw an exception", ex); } } } diff --git a/spring-aop/src/main/java/org/springframework/aop/target/CommonsPool2TargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/CommonsPool2TargetSource.java index d0f891c8b85f..8a6fcb8aae23 100644 --- a/spring-aop/src/main/java/org/springframework/aop/target/CommonsPool2TargetSource.java +++ b/spring-aop/src/main/java/org/springframework/aop/target/CommonsPool2TargetSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -79,7 +79,7 @@ public class CommonsPool2TargetSource extends AbstractPoolingTargetSource implem private boolean blockWhenExhausted = GenericObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED; /** - * The Apache Commons {@code ObjectPool} used to pool target objects + * The Apache Commons {@code ObjectPool} used to pool target objects. */ @Nullable private ObjectPool pool; diff --git a/spring-aop/src/main/java/org/springframework/aop/target/EmptyTargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/EmptyTargetSource.java index 344d33a8bb3f..4cb20236b12a 100644 --- a/spring-aop/src/main/java/org/springframework/aop/target/EmptyTargetSource.java +++ b/spring-aop/src/main/java/org/springframework/aop/target/EmptyTargetSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,9 +30,9 @@ * @author Rod Johnson * @author Juergen Hoeller */ -public class EmptyTargetSource implements TargetSource, Serializable { +public final class EmptyTargetSource implements TargetSource, Serializable { - /** use serialVersionUID from Spring 1.2 for interoperability */ + /** use serialVersionUID from Spring 1.2 for interoperability. */ private static final long serialVersionUID = 3680494563553489691L; diff --git a/spring-aop/src/main/java/org/springframework/aop/target/HotSwappableTargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/HotSwappableTargetSource.java index 9f7e5fc26399..f56c71d2c356 100644 --- a/spring-aop/src/main/java/org/springframework/aop/target/HotSwappableTargetSource.java +++ b/spring-aop/src/main/java/org/springframework/aop/target/HotSwappableTargetSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author 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,11 +37,11 @@ */ public class HotSwappableTargetSource implements TargetSource, Serializable { - /** use serialVersionUID from Spring 1.2 for interoperability */ + /** use serialVersionUID from Spring 1.2 for interoperability. */ private static final long serialVersionUID = 7497929212653839187L; - /** The current target object */ + /** The current target object. */ private Object target; diff --git a/spring-aop/src/main/java/org/springframework/aop/target/SingletonTargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/SingletonTargetSource.java index 439e59509a8e..6544ab3daff8 100644 --- a/spring-aop/src/main/java/org/springframework/aop/target/SingletonTargetSource.java +++ b/spring-aop/src/main/java/org/springframework/aop/target/SingletonTargetSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author 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,11 +37,11 @@ */ public class SingletonTargetSource implements TargetSource, Serializable { - /** use serialVersionUID from Spring 1.2 for interoperability */ + /** use serialVersionUID from Spring 1.2 for interoperability. */ private static final long serialVersionUID = 9031246629662423738L; - /** Target cached and invoked using reflection */ + /** Target cached and invoked using reflection. */ private final Object target; diff --git a/spring-aop/src/main/java/org/springframework/aop/target/dynamic/AbstractRefreshableTargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/dynamic/AbstractRefreshableTargetSource.java index 75d698db9d85..8cba76b0f7f4 100644 --- a/spring-aop/src/main/java/org/springframework/aop/target/dynamic/AbstractRefreshableTargetSource.java +++ b/spring-aop/src/main/java/org/springframework/aop/target/dynamic/AbstractRefreshableTargetSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author 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 @@ */ public abstract class AbstractRefreshableTargetSource implements TargetSource, Refreshable { - /** Logger available to subclasses */ + /** Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); @Nullable diff --git a/spring-aop/src/main/java/overview.html b/spring-aop/src/main/java/overview.html deleted file mode 100644 index 3c1e198fbd7e..000000000000 --- a/spring-aop/src/main/java/overview.html +++ /dev/null @@ -1,7 +0,0 @@ - - -

-Spring's proxy-based AOP framework. -

- - \ No newline at end of file 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 f547c026d918..60d0d4627af9 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 @@ -218,7 +218,7 @@ public void before(Method method, Object[] args, @Nullable Object target) throws itb.unreliableFileOperation(); } catch (IOException ex) { - // we don't realy care... + // we don't really care... } } 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 46f27fdde05e..3a0ca52ce70d 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 @@ -758,7 +758,7 @@ public void setAge(int a) {} @Around(value="setAge(age)",argNames="age") // @ArgNames({"age"}) // AMC needs more work here? ignoring pjp arg... ok?? - // argNames should be suported in Around as it is in Pointcut + // argNames should be supported in Around as it is in Pointcut public void changeReturnType(ProceedingJoinPoint pjp, int age) throws Throwable { pjp.proceed(new Object[] {age*2}); } @@ -884,12 +884,12 @@ public int preventExecution(ProceedingJoinPoint pjp) { @Aspect abstract class AbstractMakeModifiable { - public interface MutableModifable extends Modifiable { + public interface MutableModifiable extends Modifiable { void markDirty(); } - public static class ModifiableImpl implements MutableModifable { + public static class ModifiableImpl implements MutableModifiable { private boolean modified; @@ -911,7 +911,7 @@ public void markDirty() { @Before(value="execution(void set*(*)) && this(modifiable) && args(newValue)", argNames="modifiable,newValue") public void recordModificationIfSetterArgumentDiffersFromOldValue( - JoinPoint jp, MutableModifable mixin, Object newValue) { + JoinPoint jp, MutableModifiable mixin, Object newValue) { /* * We use the mixin to check and, if necessary, change, @@ -972,7 +972,7 @@ class MakeITestBeanModifiable extends AbstractMakeModifiable { @DeclareParents(value = "org.springframework.tests.sample.beans.ITestBean+", defaultImpl=ModifiableImpl.class) - public static MutableModifable mixin; + public static MutableModifiable mixin; } 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 a0c220d079f9..9c9552a2989d 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 @@ -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. @@ -20,7 +20,6 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.core.io.Resource; import static org.junit.Assert.*; import static org.springframework.tests.TestResourceUtils.*; @@ -33,13 +32,11 @@ */ public class TopLevelAopTagTests { - private static final Resource CONTEXT = qualifiedResource(TopLevelAopTagTests.class, "context.xml"); - @Test - public void testParse() throws Exception { + public void testParse() { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); - reader.loadBeanDefinitions(CONTEXT); + new XmlBeanDefinitionReader(beanFactory).loadBeanDefinitions( + qualifiedResource(TopLevelAopTagTests.class, "context.xml")); assertTrue(beanFactory.containsBeanDefinition("testPointcut")); } diff --git a/spring-aop/src/test/java/org/springframework/aop/framework/ClassWithConstructor.java b/spring-aop/src/test/java/org/springframework/aop/framework/ClassWithConstructor.java index e456224efb88..aa52feefa882 100644 --- a/spring-aop/src/test/java/org/springframework/aop/framework/ClassWithConstructor.java +++ b/spring-aop/src/test/java/org/springframework/aop/framework/ClassWithConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2018 the original author 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,4 +25,4 @@ public ClassWithConstructor(Object object) { public void method() { } -} \ No newline at end of file +} 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 455778c83e82..c5de9deee277 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 @@ -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. @@ -36,6 +36,7 @@ public class PrototypeTargetTests { private static final Resource CONTEXT = qualifiedResource(PrototypeTargetTests.class, "context.xml"); + @Test public void testPrototypeProxyWithPrototypeTarget() { TestBeanImpl.constructionCount = 0; @@ -64,12 +65,15 @@ public void testSingletonProxyWithPrototypeTarget() { assertEquals(10, interceptor.invocationCount); } - public static interface TestBean { - public void doSomething(); + + public interface TestBean { + + void doSomething(); } public static class TestBeanImpl implements TestBean { + private static int constructionCount = 0; public TestBeanImpl() { @@ -83,6 +87,7 @@ public void doSomething() { public static class TestInterceptor implements MethodInterceptor { + private int invocationCount = 0; @Override 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 6847a2d63a85..06f54994b537 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -216,7 +216,7 @@ public int compareTo(Object arg0) { TimeStamped ts = (TimeStamped) factory.getProxy(); assertTrue(ts.getTimeStamp() == t); // Shouldn't fail; - ((IOther) ts).absquatulate(); + ((IOther) ts).absquatulate(); } @Test 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 38f0bd9501f2..4c2735abf232 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-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. @@ -21,7 +21,6 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.core.io.Resource; import org.springframework.tests.sample.beans.ITestBean; import org.springframework.tests.sample.beans.TestBean; @@ -36,13 +35,11 @@ */ public class ExposeInvocationInterceptorTests { - private static final Resource CONTEXT = - qualifiedResource(ExposeInvocationInterceptorTests.class, "context.xml"); - @Test public void testXmlConfig() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - new XmlBeanDefinitionReader(bf).loadBeanDefinitions(CONTEXT); + new XmlBeanDefinitionReader(bf).loadBeanDefinitions( + qualifiedResource(ExposeInvocationInterceptorTests.class, "context.xml")); ITestBean tb = (ITestBean) bf.getBean("proxy"); String name = "tony"; tb.setName(name); @@ -74,6 +71,7 @@ public void absquatulate() { class InvocationCheckExposedInvocationTestBean extends ExposedInvocationTestBean { + @Override protected void assertions(MethodInvocation invocation) { assertTrue(invocation.getThis() == this); 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 4b5634f2b0aa..67e1c30ddce2 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 @@ -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. @@ -22,7 +22,6 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.core.io.Resource; import static org.junit.Assert.*; import static org.springframework.tests.TestResourceUtils.*; @@ -34,16 +33,12 @@ */ public class ScopedProxyAutowireTests { - private static final Resource SCOPED_AUTOWIRE_FALSE_CONTEXT = - qualifiedResource(ScopedProxyAutowireTests.class, "scopedAutowireFalse.xml"); - private static final Resource SCOPED_AUTOWIRE_TRUE_CONTEXT = - qualifiedResource(ScopedProxyAutowireTests.class, "scopedAutowireTrue.xml"); - - @Test public void testScopedProxyInheritsAutowireCandidateFalse() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - new XmlBeanDefinitionReader(bf).loadBeanDefinitions(SCOPED_AUTOWIRE_FALSE_CONTEXT); + new XmlBeanDefinitionReader(bf).loadBeanDefinitions( + qualifiedResource(ScopedProxyAutowireTests.class, "scopedAutowireFalse.xml")); + assertTrue(Arrays.asList(bf.getBeanNamesForType(TestBean.class, false, false)).contains("scoped")); assertTrue(Arrays.asList(bf.getBeanNamesForType(TestBean.class, true, false)).contains("scoped")); assertFalse(bf.containsSingleton("scoped")); @@ -55,7 +50,9 @@ public void testScopedProxyInheritsAutowireCandidateFalse() { @Test public void testScopedProxyReplacesAutowireCandidateTrue() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); - new XmlBeanDefinitionReader(bf).loadBeanDefinitions(SCOPED_AUTOWIRE_TRUE_CONTEXT); + new XmlBeanDefinitionReader(bf).loadBeanDefinitions( + qualifiedResource(ScopedProxyAutowireTests.class, "scopedAutowireTrue.xml")); + assertTrue(Arrays.asList(bf.getBeanNamesForType(TestBean.class, true, false)).contains("scoped")); assertTrue(Arrays.asList(bf.getBeanNamesForType(TestBean.class, false, false)).contains("scoped")); assertFalse(bf.containsSingleton("scoped")); 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 fa90846d9b5a..072b709374cb 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -157,7 +157,7 @@ public long getTimeStamp() { TimeStamped ts = (TimeStamped) pf.getProxy(); assertThat(ts, instanceOf(TimeStamped.class)); - // Shoulnd't proxy framework interfaces + // Shouldn't proxy framework interfaces assertTrue(!(ts instanceof MethodInterceptor)); assertTrue(!(ts instanceof IntroductionInterceptor)); 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 2bfa7e6a039d..e7728a2d307d 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-2013 the original author or authors. + * Copyright 2002-2018 the original author 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,11 +41,12 @@ public class NameMatchMethodPointcutTests { protected SerializableNopInterceptor nop; + /** * Create an empty pointcut, populating instance variables. */ @Before - public void setUp() { + public void setup() { ProxyFactory pf = new ProxyFactory(new SerializablePerson()); nop = new SerializableNopInterceptor(); pc = new NameMatchMethodPointcut(); @@ -53,6 +54,7 @@ public void setUp() { proxied = (Person) pf.getProxy(); } + @Test public void testMatchingOnly() { // Can't do exact matching through isMatch @@ -94,7 +96,7 @@ public void testMatchOneMethod() throws Throwable { @Test public void testSets() throws Throwable { - pc.setMappedNames(new String[] { "set*", "echo" }); + pc.setMappedNames("set*", "echo"); assertEquals(0, nop.getCount()); proxied.getName(); proxied.setName(""); @@ -116,7 +118,7 @@ public void testSerializable() throws Throwable { } @Test - public void testEqualsAndHashCode() throws Exception { + public void testEqualsAndHashCode() { NameMatchMethodPointcut pc1 = new NameMatchMethodPointcut(); NameMatchMethodPointcut pc2 = new NameMatchMethodPointcut(); 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 68b9d26b9cec..b1a5b478d5cc 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-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. @@ -39,7 +39,8 @@ public class RegexpMethodPointcutAdvisorIntegrationTests { private static final Resource CONTEXT = - qualifiedResource(RegexpMethodPointcutAdvisorIntegrationTests.class, "context.xml"); + qualifiedResource(RegexpMethodPointcutAdvisorIntegrationTests.class, "context.xml"); + @Test public void testSinglePattern() throws Throwable { 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 d79ae01e8c9b..051340bed9b3 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-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. @@ -25,7 +25,6 @@ import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.core.io.Resource; import org.springframework.tests.aop.interceptor.SerializableNopInterceptor; import org.springframework.tests.sample.beans.Person; import org.springframework.tests.sample.beans.SerializablePerson; @@ -41,31 +40,31 @@ */ public class HotSwappableTargetSourceTests { - private static final Resource CONTEXT = qualifiedResource(HotSwappableTargetSourceTests.class, "context.xml"); - /** Initial count value set in bean factory XML */ private static final int INITIAL_COUNT = 10; private DefaultListableBeanFactory beanFactory; + @Before - public void setUp() throws Exception { + public void setup() { this.beanFactory = new DefaultListableBeanFactory(); - new XmlBeanDefinitionReader(this.beanFactory).loadBeanDefinitions(CONTEXT); + new XmlBeanDefinitionReader(this.beanFactory).loadBeanDefinitions( + qualifiedResource(HotSwappableTargetSourceTests.class, "context.xml")); } /** * We must simulate container shutdown, which should clear threads. */ @After - public void tearDown() { + public void close() { // Will call pool.close() this.beanFactory.destroySingletons(); } + /** * Check it works like a normal invoker - * */ @Test public void testBasicFunctionality() { @@ -106,18 +105,13 @@ public void testValidSwaps() { assertEquals(target1.getCount(), proxied.getCount()); } - - /** - * - * @param invalid - * @return the message - */ - private IllegalArgumentException testRejectsSwapToInvalidValue(Object invalid) { + @Test + public void testRejectsSwapToNull() { HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper"); IllegalArgumentException aopex = null; try { - swapper.swap(invalid); - fail("Shouldn't be able to swap to invalid value [" + invalid + "]"); + swapper.swap(null); + fail("Shouldn't be able to swap to invalid value"); } catch (IllegalArgumentException ex) { // Ok @@ -126,19 +120,9 @@ private IllegalArgumentException testRejectsSwapToInvalidValue(Object invalid) { // It shouldn't be corrupted, it should still work testBasicFunctionality(); - return aopex; + assertTrue(aopex.getMessage().contains("null")); } - @Test - public void testRejectsSwapToNull() { - IllegalArgumentException ex = testRejectsSwapToInvalidValue(null); - assertTrue(ex.getMessage().indexOf("null") != -1); - } - - // TODO test reject swap to wrong interface or class? - // how to decide what's valid? - - @Test public void testSerialization() throws Exception { SerializablePerson sp1 = new SerializablePerson(); @@ -165,4 +149,5 @@ public void testSerialization() throws Exception { assertEquals(sp1.getName(), p.getName()); } + } 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 995ee9f89ac6..528dce52aa00 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 @@ -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,11 +19,8 @@ import org.junit.Before; import org.junit.Test; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.core.io.Resource; import org.springframework.tests.sample.beans.SideEffectBean; import static org.junit.Assert.*; @@ -35,19 +32,20 @@ */ public class PrototypeTargetSourceTests { - private static final Resource CONTEXT = qualifiedResource(PrototypeTargetSourceTests.class, "context.xml"); - /** Initial count value set in bean factory XML */ private static final int INITIAL_COUNT = 10; - private BeanFactory beanFactory; + private DefaultListableBeanFactory beanFactory; + @Before - public void setUp() throws Exception { + public void setup() { this.beanFactory = new DefaultListableBeanFactory(); - new XmlBeanDefinitionReader((BeanDefinitionRegistry) this.beanFactory).loadBeanDefinitions(CONTEXT); + new XmlBeanDefinitionReader(this.beanFactory).loadBeanDefinitions( + qualifiedResource(PrototypeTargetSourceTests.class, "context.xml")); } + /** * Test that multiple invocations of the prototype bean will result * in no change to visible state, as a new instance is used. @@ -66,5 +64,4 @@ public void testPrototypeAndSingletonBehaveDifferently() { assertEquals(INITIAL_COUNT, prototype.getCount()); } - } 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 0ff97aa8a981..baa6005d0f8c 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 @@ -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. @@ -21,7 +21,6 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.core.io.Resource; import org.springframework.tests.sample.beans.ITestBean; import org.springframework.tests.sample.beans.SideEffectBean; @@ -34,26 +33,27 @@ */ public class ThreadLocalTargetSourceTests { - private static final Resource CONTEXT = qualifiedResource(ThreadLocalTargetSourceTests.class, "context.xml"); - /** Initial count value set in bean factory XML */ private static final int INITIAL_COUNT = 10; private DefaultListableBeanFactory beanFactory; + @Before - public void setUp() throws Exception { + public void setup() { this.beanFactory = new DefaultListableBeanFactory(); - new XmlBeanDefinitionReader(this.beanFactory).loadBeanDefinitions(CONTEXT); + new XmlBeanDefinitionReader(this.beanFactory).loadBeanDefinitions( + qualifiedResource(ThreadLocalTargetSourceTests.class, "context.xml")); } /** * We must simulate container shutdown, which should clear threads. */ - protected void tearDown() { + protected void close() { this.beanFactory.destroySingletons(); } + /** * Check we can use two different ThreadLocalTargetSources * managing objects of different types without them interfering @@ -141,7 +141,7 @@ public void run() { } /** - * Test for SPR-1442. Destroyed target should re-associated with thread and not throw NPE + * Test for SPR-1442. Destroyed target should re-associated with thread and not throw NPE. */ @Test public void testReuseDestroyedTarget() { diff --git a/spring-aop/src/test/resources/log4j2-test.xml b/spring-aop/src/test/resources/log4j2-test.xml index 78b880bbcd83..521cc44e65a1 100644 --- a/spring-aop/src/test/resources/log4j2-test.xml +++ b/spring-aop/src/test/resources/log4j2-test.xml @@ -2,7 +2,7 @@ - + diff --git a/spring-aspects/spring-aspects.gradle b/spring-aspects/spring-aspects.gradle index b19daa364f4f..51bab8a81983 100644 --- a/spring-aspects/spring-aspects.gradle +++ b/spring-aspects/spring-aspects.gradle @@ -93,7 +93,7 @@ dependencies { optional("javax.transaction:javax.transaction-api:1.3") // for @javax.transaction.Transactional support testCompile(project(":spring-core")) // for CodeStyleAspect testCompile(project(":spring-test")) - testCompile("javax.mail:javax.mail-api:1.6.1") + testCompile("javax.mail:javax.mail-api:1.6.2") } eclipse.project { diff --git a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AnyThrow.java b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AnyThrow.java index d391c0a6bc84..167cff4a437b 100644 --- a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AnyThrow.java +++ b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AnyThrow.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2018 the original author 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,11 @@ * * @author Stephane Nicoll */ -class AnyThrow { +final class AnyThrow { + + private AnyThrow() { + } + static void throwUnchecked(Throwable e) { AnyThrow.throwAny(e); 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 32e5b77a1557..7a47d2e1c4c7 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 @@ -29,6 +29,7 @@ * * @author Chris Beams * @author Stephane Nicoll + * @author Juergen Hoeller * @since 3.1 * @see org.springframework.cache.annotation.EnableCaching * @see org.springframework.cache.annotation.CachingConfigurationSelector @@ -41,18 +42,7 @@ public class AspectJCachingConfiguration extends AbstractCachingConfiguration { @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public AnnotationCacheAspect cacheAspect() { AnnotationCacheAspect cacheAspect = AnnotationCacheAspect.aspectOf(); - if (this.cacheResolver != null) { - cacheAspect.setCacheResolver(this.cacheResolver); - } - else if (this.cacheManager != null) { - cacheAspect.setCacheManager(this.cacheManager); - } - if (this.keyGenerator != null) { - cacheAspect.setKeyGenerator(this.keyGenerator); - } - if (this.errorHandler != null) { - cacheAspect.setErrorHandler(this.errorHandler); - } + cacheAspect.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager); return cacheAspect; } diff --git a/spring-aspects/src/main/java/org/springframework/context/annotation/aspectj/SpringConfiguredConfiguration.java b/spring-aspects/src/main/java/org/springframework/context/annotation/aspectj/SpringConfiguredConfiguration.java index 1d141b2ee921..05cd5e4caf36 100644 --- a/spring-aspects/src/main/java/org/springframework/context/annotation/aspectj/SpringConfiguredConfiguration.java +++ b/spring-aspects/src/main/java/org/springframework/context/annotation/aspectj/SpringConfiguredConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2018 the original author 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,6 +39,9 @@ @Configuration public class SpringConfiguredConfiguration { + /** + * The bean name used for the configurer aspect. + */ public static final String BEAN_CONFIGURER_ASPECT_BEAN_NAME = "org.springframework.context.config.internalBeanConfigurerAspect"; 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 bb8128d8e14b..539fcb9f2582 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-2014 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ * * @author Chris Beams * @author Stephane Nicoll + * @author Juergen Hoeller * @since 3.1 * @see EnableAsync * @see org.springframework.scheduling.annotation.AsyncConfigurationSelector @@ -42,12 +43,7 @@ public class AspectJAsyncConfiguration extends AbstractAsyncConfiguration { @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public AnnotationAsyncExecutionAspect asyncAdvisor() { AnnotationAsyncExecutionAspect asyncAspect = AnnotationAsyncExecutionAspect.aspectOf(); - if (this.executor != null) { - asyncAspect.setExecutor(this.executor); - } - if (this.exceptionHandler != null) { - asyncAspect.setExceptionHandler(this.exceptionHandler); - } + asyncAspect.configure(this.executor, this.exceptionHandler); return asyncAspect; } 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 new file mode 100644 index 000000000000..fb644851f1af --- /dev/null +++ b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/AspectJJtaTransactionManagementConfiguration.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2018 the original author 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 + * + * http://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.transaction.aspectj; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Role; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.transaction.annotation.TransactionManagementConfigurationSelector; +import org.springframework.transaction.config.TransactionManagementConfigUtils; + +/** + * {@code @Configuration} class that registers the Spring infrastructure beans necessary + * to enable AspectJ-based annotation-driven transaction management for the JTA 1.2 + * {@link javax.transaction.Transactional} annotation in addition to Spring's own + * {@link org.springframework.transaction.annotation.Transactional} annotation. + * + * @author Juergen Hoeller + * @since 5.1 + * @see EnableTransactionManagement + * @see TransactionManagementConfigurationSelector + */ +@Configuration +public class AspectJJtaTransactionManagementConfiguration extends AspectJTransactionManagementConfiguration { + + @Bean(name = TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_BEAN_NAME) + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public JtaAnnotationTransactionAspect jtaTransactionAspect() { + JtaAnnotationTransactionAspect txAspect = JtaAnnotationTransactionAspect.aspectOf(); + if (this.txManager != null) { + txAspect.setTransactionManager(this.txManager); + } + return txAspect; + } + +} 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 41993f81d477..e0662bd6a8da 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-2014 the original author or authors. + * Copyright 2002-2018 the original author 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,12 +27,15 @@ /** * {@code @Configuration} class that registers the Spring infrastructure beans necessary - * to enable AspectJ-based annotation-driven transaction management. + * to enable AspectJ-based annotation-driven transaction management for Spring's own + * {@link org.springframework.transaction.annotation.Transactional} annotation. * * @author Chris Beams + * @author Juergen Hoeller * @since 3.1 * @see EnableTransactionManagement * @see TransactionManagementConfigurationSelector + * @see AspectJJtaTransactionManagementConfiguration */ @Configuration public class AspectJTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { diff --git a/spring-aspects/src/main/java/org/springframework/transaction/aspectj/JtaAnnotationTransactionAspect.aj b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/JtaAnnotationTransactionAspect.aj index 6b02e1de3a5d..22a3bb134b55 100644 --- a/spring-aspects/src/main/java/org/springframework/transaction/aspectj/JtaAnnotationTransactionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/JtaAnnotationTransactionAspect.aj @@ -45,7 +45,7 @@ import org.springframework.transaction.annotation.AnnotationTransactionAttribute * @see javax.transaction.Transactional * @see AnnotationTransactionAspect */ -@RequiredTypes({"javax.transaction.Transactional"}) +@RequiredTypes("javax.transaction.Transactional") public aspect JtaAnnotationTransactionAspect extends AbstractTransactionAspect { public JtaAnnotationTransactionAspect() { diff --git a/spring-aspects/src/main/java/overview.html b/spring-aspects/src/main/java/overview.html deleted file mode 100644 index 2dd5b0922405..000000000000 --- a/spring-aspects/src/main/java/overview.html +++ /dev/null @@ -1,7 +0,0 @@ - - -

-Spring's AspectJ-based aspects. -

- - 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 15680c9ac683..269f607dbb11 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -120,7 +120,7 @@ public void noCacheManagerBeans() { load(EmptyConfig.class); } catch (IllegalStateException ex) { - assertTrue(ex.getMessage().contains("No bean of type CacheManager")); + assertTrue(ex.getMessage().contains("no bean of type CacheManager")); } } diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/SomeCustomKeyGenerator.java b/spring-aspects/src/test/java/org/springframework/cache/config/SomeCustomKeyGenerator.java index 854240e17df4..58efd997c5ee 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/SomeCustomKeyGenerator.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/SomeCustomKeyGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author 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,20 +28,20 @@ */ public class SomeCustomKeyGenerator implements KeyGenerator { - @Override - public Object generate(Object target, Method method, Object... params) { - return generateKey(method.getName(), params); - } + @Override + public Object generate(Object target, Method method, Object... params) { + return generateKey(method.getName(), params); + } - /** - * @see #generate(Object, java.lang.reflect.Method, Object...) - */ - static Object generateKey(String methodName, Object... params) { - final StringBuilder sb = new StringBuilder(methodName); - for (Object param : params) { - sb.append(param); - } - return sb.toString(); - } + /** + * @see #generate(Object, java.lang.reflect.Method, Object...) + */ + static Object generateKey(String methodName, Object... params) { + final StringBuilder sb = new StringBuilder(methodName); + for (Object param : params) { + sb.append(param); + } + return sb.toString(); + } } 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 1d7d1af4a0bf..e119458cd66d 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -295,7 +295,7 @@ static class ClassWithException { @Async public void failWithVoid() { - throw new UnsupportedOperationException("failWithVoid"); + throw new UnsupportedOperationException("failWithVoid"); } } diff --git a/spring-aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationDrivenBeanDefinitionParserTests.java b/spring-aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationDrivenBeanDefinitionParserTests.java index 4b609c6e2c8c..a40a766dee08 100644 --- a/spring-aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationDrivenBeanDefinitionParserTests.java +++ b/spring-aspects/src/test/java/org/springframework/scheduling/aspectj/AnnotationDrivenBeanDefinitionParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2018 the original author 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.scheduling.aspectj; +import java.util.function.Supplier; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -53,17 +55,19 @@ public void asyncAspectRegistered() { } @Test + @SuppressWarnings("rawtypes") public void asyncPostProcessorExecutorReference() { Object executor = context.getBean("testExecutor"); Object aspect = context.getBean(TaskManagementConfigUtils.ASYNC_EXECUTION_ASPECT_BEAN_NAME); - assertSame(executor, new DirectFieldAccessor(aspect).getPropertyValue("defaultExecutor")); + assertSame(executor, ((Supplier) new DirectFieldAccessor(aspect).getPropertyValue("defaultExecutor")).get()); } @Test + @SuppressWarnings("rawtypes") public void asyncPostProcessorExceptionHandlerReference() { Object exceptionHandler = context.getBean("testExceptionHandler"); Object aspect = context.getBean(TaskManagementConfigUtils.ASYNC_EXECUTION_ASPECT_BEAN_NAME); - assertSame(exceptionHandler, new DirectFieldAccessor(aspect).getPropertyValue("exceptionHandler")); + assertSame(exceptionHandler, ((Supplier) new DirectFieldAccessor(aspect).getPropertyValue("exceptionHandler")).get()); } } diff --git a/spring-aspects/src/test/resources/log4j2-test.xml b/spring-aspects/src/test/resources/log4j2-test.xml index f5e85a2eac86..fb1c94a4807d 100644 --- a/spring-aspects/src/test/resources/log4j2-test.xml +++ b/spring-aspects/src/test/resources/log4j2-test.xml @@ -2,7 +2,7 @@ - + diff --git a/spring-beans/spring-beans.gradle b/spring-beans/spring-beans.gradle index b8ead1ac61b3..b7524b5ee116 100644 --- a/spring-beans/spring-beans.gradle +++ b/spring-beans/spring-beans.gradle @@ -3,17 +3,16 @@ description = "Spring Beans" apply plugin: "groovy" dependencies { - compile(project(':spring-core')) + compile(project(":spring-core")) optional("javax.inject:javax.inject:1") - optional("org.yaml:snakeyaml:1.21") - optional("org.codehaus.groovy:groovy-all:${groovyVersion}") + optional("org.yaml:snakeyaml:1.24") + optional("org.codehaus.groovy:groovy-xml:${groovyVersion}") optional("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}") optional("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}") testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}") } -// This modules does joint compilation for Java and Groovy code, -// with the compileGroovy task. +// This module does joint compilation for Java and Groovy code with the compileGroovy task. sourceSets { main.groovy.srcDirs += "src/main/java" main.java.srcDirs = [] @@ -24,10 +23,9 @@ compileGroovy { targetCompatibility = 1.8 } -// This module also builds Kotlin code and the compileKotlin task -// naturally depends on compileJava. -// We need to redefine dependencies to break task cycles. +// 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' +compileGroovy.dependsOn = deps - "compileJava" compileKotlin.dependsOn(compileGroovy) compileKotlin.classpath += files(compileGroovy.destinationDir) 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 5b6ef07aac13..0f63e2114cd3 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-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -86,7 +86,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA @Nullable Object rootObject; - /** Map with cached nested Accessors: nested path -> Accessor instance */ + /** Map with cached nested Accessors: nested path -> Accessor instance. */ @Nullable private Map nestedPropertyAccessors; @@ -194,7 +194,7 @@ public void setWrappedInstance(Object object, @Nullable String nestedPath, @Null this.wrappedObject = ObjectUtils.unwrapOptional(object); Assert.notNull(this.wrappedObject, "Target object must not be null"); this.nestedPath = (nestedPath != null ? nestedPath : ""); - this.rootObject = (!"".equals(this.nestedPath) ? rootObject : this.wrappedObject); + this.rootObject = (!this.nestedPath.isEmpty() ? rootObject : this.wrappedObject); this.nestedPropertyAccessors = null; this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject); } @@ -971,6 +971,9 @@ public String toString() { } + /** + * A handler for a specific property. + */ protected abstract static class PropertyHandler { private final Class propertyType; @@ -1026,6 +1029,9 @@ public Class getCollectionType(int nestingLevel) { } + /** + * Holder class used to store property tokens. + */ protected static class PropertyTokenHolder { public PropertyTokenHolder(String name) { 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 71395c9328bb..3f88f663457f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java @@ -16,8 +16,8 @@ package org.springframework.beans; +import java.util.ArrayList; import java.util.Arrays; -import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -110,7 +110,7 @@ public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean } catch (PropertyAccessException ex) { if (propertyAccessExceptions == null) { - propertyAccessExceptions = new LinkedList<>(); + propertyAccessExceptions = new ArrayList<>(); } propertyAccessExceptions.add(ex); } diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanInstantiationException.java b/spring-beans/src/main/java/org/springframework/beans/BeanInstantiationException.java index 4f11119f588c..0c23f985407c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanInstantiationException.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanInstantiationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,13 +31,13 @@ @SuppressWarnings("serial") public class BeanInstantiationException extends FatalBeanException { - private Class beanClass; + private final Class beanClass; @Nullable - private Constructor constructor; + private final Constructor constructor; @Nullable - private Method constructingMethod; + private final Method constructingMethod; /** @@ -58,6 +58,8 @@ public BeanInstantiationException(Class beanClass, String msg) { public BeanInstantiationException(Class beanClass, String msg, @Nullable Throwable cause) { super("Failed to instantiate [" + beanClass.getName() + "]: " + msg, cause); this.beanClass = beanClass; + this.constructor = null; + this.constructingMethod = null; } /** @@ -71,6 +73,7 @@ public BeanInstantiationException(Constructor constructor, String msg, @Nulla super("Failed to instantiate [" + constructor.getDeclaringClass().getName() + "]: " + msg, cause); this.beanClass = constructor.getDeclaringClass(); this.constructor = constructor; + this.constructingMethod = null; } /** @@ -84,6 +87,7 @@ public BeanInstantiationException(Constructor constructor, String msg, @Nulla public BeanInstantiationException(Method constructingMethod, String msg, @Nullable Throwable cause) { super("Failed to instantiate [" + constructingMethod.getReturnType().getName() + "]: " + msg, cause); this.beanClass = constructingMethod.getReturnType(); + this.constructor = null; this.constructingMethod = constructingMethod; } 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 f9712f4af270..75559a1c0727 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-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. @@ -70,6 +70,18 @@ public abstract class BeanUtils { private static final Set> unknownEditorTypes = Collections.newSetFromMap(new ConcurrentReferenceHashMap<>(64)); + private static final Map, Object> DEFAULT_TYPE_VALUES; + + static { + Map, Object> values = new HashMap<>(); + values.put(boolean.class, false); + values.put(byte.class, (byte) 0); + values.put(short.class, (short) 0); + values.put(int.class, 0); + values.put(long.class, (long) 0); + DEFAULT_TYPE_VALUES = Collections.unmodifiableMap(values); + } + /** * Convenience method to instantiate a class using its no-arg constructor. @@ -119,11 +131,13 @@ public static T instantiateClass(Class clazz) throws BeanInstantiationExc throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { - Constructor ctor = (KotlinDetector.isKotlinType(clazz) ? - KotlinDelegate.getPrimaryConstructor(clazz) : clazz.getDeclaredConstructor()); - return instantiateClass(ctor); + return instantiateClass(clazz.getDeclaredConstructor()); } catch (NoSuchMethodException ex) { + Constructor ctor = findPrimaryConstructor(clazz); + if (ctor != null) { + return instantiateClass(ctor); + } throw new BeanInstantiationException(clazz, "No default constructor found", ex); } catch (LinkageError err) { @@ -157,7 +171,7 @@ public static T instantiateClass(Class clazz, Class assignableTo) thro * with optional parameters and default values. * @param ctor the constructor to instantiate * @param args the constructor arguments to apply (use {@code null} for an unspecified - * parameter if needed for Kotlin classes with optional parameters and default values) + * parameter, Kotlin optional parameters and Java primitive types are supported) * @return the new instance * @throws BeanInstantiationException if the bean cannot be instantiated * @see Constructor#newInstance @@ -166,8 +180,24 @@ public static T instantiateClass(Constructor ctor, Object... args) throws Assert.notNull(ctor, "Constructor must not be null"); try { ReflectionUtils.makeAccessible(ctor); - return (KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ? - KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args)); + if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass())) { + return KotlinDelegate.instantiateClass(ctor, args); + } + else { + Class[] parameterTypes = ctor.getParameterTypes(); + Assert.isTrue(args.length <= parameterTypes.length, "Can't specify more arguments than constructor parameters"); + Object[] argsWithDefaultValues = new Object[args.length]; + for (int i = 0 ; i < args.length; i++) { + if (args[i] == null) { + Class parameterType = parameterTypes[i]; + argsWithDefaultValues[i] = (parameterType.isPrimitive() ? DEFAULT_TYPE_VALUES.get(parameterType) : null); + } + else { + argsWithDefaultValues[i] = args[i]; + } + } + return ctor.newInstance(argsWithDefaultValues); + } } catch (InstantiationException ex) { throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex); @@ -196,7 +226,7 @@ public static T instantiateClass(Constructor ctor, Object... args) throws @Nullable public static Constructor findPrimaryConstructor(Class clazz) { Assert.notNull(clazz, "Class must not be null"); - if (KotlinDetector.isKotlinType(clazz)) { + if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(clazz)) { Constructor kotlinPrimaryConstructor = KotlinDelegate.findPrimaryConstructor(clazz); if (kotlinPrimaryConstructor != null) { return kotlinPrimaryConstructor; @@ -496,8 +526,8 @@ public static PropertyEditor findEditorByConvention(@Nullable Class targetTyp try { Class editorClass = cl.loadClass(editorName); if (!PropertyEditor.class.isAssignableFrom(editorClass)) { - if (logger.isWarnEnabled()) { - logger.warn("Editor class [" + editorName + + if (logger.isInfoEnabled()) { + logger.info("Editor class [" + editorName + "] does not implement [java.beans.PropertyEditor] interface"); } unknownEditorTypes.add(targetType); @@ -506,8 +536,8 @@ public static PropertyEditor findEditorByConvention(@Nullable Class targetTyp return (PropertyEditor) instantiateClass(editorClass); } catch (ClassNotFoundException ex) { - if (logger.isDebugEnabled()) { - logger.debug("No property editor [" + editorName + "] found for type " + + if (logger.isTraceEnabled()) { + logger.trace("No property editor [" + editorName + "] found for type " + targetType.getName() + " according to 'Editor' suffix convention"); } unknownEditorTypes.add(targetType); @@ -699,22 +729,6 @@ private static void copyProperties(Object source, Object target, @Nullable Class */ private static class KotlinDelegate { - /** - * Determine the Java constructor corresponding to the Kotlin primary constructor. - * @param clazz the {@link Class} of the Kotlin class - * @throws NoSuchMethodException if no such constructor found - * @since 5.0.3 - * @see #findPrimaryConstructor - * @see Class#getDeclaredConstructor - */ - public static Constructor getPrimaryConstructor(Class clazz) throws NoSuchMethodException { - Constructor ctor = findPrimaryConstructor(clazz); - if (ctor == null) { - throw new NoSuchMethodException(); - } - return ctor; - } - /** * Retrieve the Java constructor corresponding to the Kotlin primary constructor, if any. * @param clazz the {@link Class} of the Kotlin class 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 7aaf0d470dbf..109fbca842c7 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java @@ -70,7 +70,7 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements private CachedIntrospectionResults cachedIntrospectionResults; /** - * The security context used for invoking the property methods + * The security context used for invoking the property methods. */ @Nullable private AccessControlContext acc; @@ -166,7 +166,7 @@ protected void setIntrospectionClass(Class clazz) { } /** - * Obtain a lazily initializted CachedIntrospectionResults instance + * Obtain a lazily initialized CachedIntrospectionResults instance * for the wrapped object. */ private CachedIntrospectionResults getCachedIntrospectionResults() { @@ -283,7 +283,7 @@ public TypeDescriptor toTypeDescriptor() { @Override @Nullable public TypeDescriptor nested(int level) { - return TypeDescriptor.nested(property(pd), level); + return TypeDescriptor.nested(property(this.pd), level); } @Override 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 8656d9e70236..804ff29440e0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java +++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java @@ -71,7 +71,7 @@ * @see #clearClassLoader(ClassLoader) * @see #forClass(Class) */ -public class CachedIntrospectionResults { +public final class CachedIntrospectionResults { /** * System property that instructs Spring to use the {@link Introspector#IGNORE_ALL_BEANINFO} @@ -96,7 +96,7 @@ public class CachedIntrospectionResults { private static final boolean shouldIntrospectorIgnoreBeaninfoClasses = SpringProperties.getFlag(IGNORE_BEANINFO_PROPERTY_NAME); - /** Stores the BeanInfoFactory instances */ + /** Stores the BeanInfoFactory instances. */ private static List beanInfoFactories = SpringFactoriesLoader.loadFactories( BeanInfoFactory.class, CachedIntrospectionResults.class.getClassLoader()); @@ -250,13 +250,13 @@ private static BeanInfo getBeanInfo(Class beanClass) throws IntrospectionExce } - /** The BeanInfo object for the introspected bean class */ + /** The BeanInfo object for the introspected bean class. */ private final BeanInfo beanInfo; - /** PropertyDescriptor objects keyed by property name String */ + /** PropertyDescriptor objects keyed by property name String. */ private final Map propertyDescriptorCache; - /** TypeDescriptor objects keyed by PropertyDescriptor */ + /** TypeDescriptor objects keyed by PropertyDescriptor. */ private final ConcurrentMap typeDescriptorCache; @@ -297,20 +297,10 @@ private CachedIntrospectionResults(Class beanClass) throws BeansException { // Explicitly check implemented interfaces for setter/getter methods as well, // in particular for Java 8 default methods... - Class clazz = beanClass; - while (clazz != null && clazz != Object.class) { - Class[] ifcs = clazz.getInterfaces(); - for (Class ifc : ifcs) { - if (!ClassUtils.isJavaLanguageInterface(ifc)) { - for (PropertyDescriptor pd : getBeanInfo(ifc).getPropertyDescriptors()) { - if (!this.propertyDescriptorCache.containsKey(pd.getName())) { - pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd); - this.propertyDescriptorCache.put(pd.getName(), pd); - } - } - } - } - clazz = clazz.getSuperclass(); + Class currClass = beanClass; + while (currClass != null && currClass != Object.class) { + introspectInterfaces(beanClass, currClass); + currClass = currClass.getSuperclass(); } this.typeDescriptorCache = new ConcurrentReferenceHashMap<>(); @@ -320,6 +310,24 @@ private CachedIntrospectionResults(Class beanClass) throws BeansException { } } + private void introspectInterfaces(Class beanClass, Class currClass) throws IntrospectionException { + for (Class ifc : currClass.getInterfaces()) { + if (!ClassUtils.isJavaLanguageInterface(ifc)) { + for (PropertyDescriptor pd : getBeanInfo(ifc).getPropertyDescriptors()) { + PropertyDescriptor existingPd = this.propertyDescriptorCache.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); + } + } + introspectInterfaces(ifc, ifc); + } + } + } + BeanInfo getBeanInfo() { return this.beanInfo; 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 2f87b10a3657..1dd324ce8167 100644 --- a/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java +++ b/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java @@ -43,8 +43,10 @@ * 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: + * *
  * public class Bean {
+ *
  *     private Foo foo;
  *
  *     public Foo getFoo() {
@@ -56,6 +58,7 @@
  *         return this;
  *     }
  * }
+ * * The standard JavaBeans {@code Introspector} will discover the {@code getFoo} read * method, but will bypass the {@code #setFoo(Foo)} write method, because its non-void * returning signature does not comply with the JavaBeans specification. @@ -68,6 +71,7 @@ * indexed properties are fully supported. * * @author Chris Beams + * @author Juergen Hoeller * @since 3.1 * @see #ExtendedBeanInfo(BeanInfo) * @see ExtendedBeanInfoFactory @@ -79,8 +83,7 @@ class ExtendedBeanInfo implements BeanInfo { private final BeanInfo delegate; - private final Set propertyDescriptors = - new TreeSet<>(new PropertyDescriptorComparator()); + private final Set propertyDescriptors = new TreeSet<>(new PropertyDescriptorComparator()); /** @@ -91,11 +94,9 @@ class ExtendedBeanInfo implements BeanInfo { * through its method descriptors to find any non-void returning write methods and * update or create the corresponding {@link PropertyDescriptor} for each one found. * @param delegate the wrapped {@code BeanInfo}, which is never modified - * @throws IntrospectionException if any problems occur creating and adding new - * property descriptors * @see #getPropertyDescriptors() */ - public ExtendedBeanInfo(BeanInfo delegate) throws IntrospectionException { + public ExtendedBeanInfo(BeanInfo delegate) { this.delegate = delegate; for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) { try { @@ -213,9 +214,9 @@ private String propertyNameFor(Method method) { /** - * Return the set of {@link PropertyDescriptor}s from the wrapped {@link BeanInfo} - * object as well as {@code PropertyDescriptor}s for each non-void returning setter - * method found during construction. + * Return the set of {@link PropertyDescriptor PropertyDescriptors} from the wrapped + * {@link BeanInfo} object as well as {@code PropertyDescriptors} for each non-void + * returning setter method found during construction. * @see #ExtendedBeanInfo(BeanInfo) */ @Override @@ -259,6 +260,9 @@ public MethodDescriptor[] getMethodDescriptors() { } + /** + * A simple {@link PropertyDescriptor}. + */ static class SimplePropertyDescriptor extends PropertyDescriptor { @Nullable @@ -278,7 +282,9 @@ public SimplePropertyDescriptor(PropertyDescriptor original) throws Introspectio PropertyDescriptorUtils.copyNonMethodProperties(original, this); } - public SimplePropertyDescriptor(String propertyName, @Nullable Method readMethod, Method writeMethod) throws IntrospectionException { + public SimplePropertyDescriptor(String propertyName, @Nullable Method readMethod, Method writeMethod) + throws IntrospectionException { + super(propertyName, null, null); this.readMethod = readMethod; this.writeMethod = writeMethod; @@ -308,6 +314,7 @@ public void setWriteMethod(@Nullable Method writeMethod) { } @Override + @Nullable public Class getPropertyType() { if (this.propertyType == null) { try { @@ -350,6 +357,9 @@ public String toString() { } + /** + * A simple {@link IndexedPropertyDescriptor}. + */ static class SimpleIndexedPropertyDescriptor extends IndexedPropertyDescriptor { @Nullable @@ -379,8 +389,9 @@ public SimpleIndexedPropertyDescriptor(IndexedPropertyDescriptor original) throw PropertyDescriptorUtils.copyNonMethodProperties(original, this); } - public SimpleIndexedPropertyDescriptor(String propertyName, @Nullable Method readMethod, @Nullable Method writeMethod, - @Nullable Method indexedReadMethod, Method indexedWriteMethod) throws IntrospectionException { + public SimpleIndexedPropertyDescriptor(String propertyName, @Nullable Method readMethod, + @Nullable Method writeMethod, @Nullable Method indexedReadMethod, Method indexedWriteMethod) + throws IntrospectionException { super(propertyName, null, null, null, null); this.readMethod = readMethod; @@ -415,6 +426,7 @@ public void setWriteMethod(@Nullable Method writeMethod) { } @Override + @Nullable public Class getPropertyType() { if (this.propertyType == null) { try { @@ -450,6 +462,7 @@ public void setIndexedWriteMethod(@Nullable Method indexedWriteMethod) throws In } @Override + @Nullable public Class getIndexedPropertyType() { if (this.indexedPropertyType == null) { try { diff --git a/spring-beans/src/main/java/org/springframework/beans/InvalidPropertyException.java b/spring-beans/src/main/java/org/springframework/beans/InvalidPropertyException.java index 8cb121413087..7b7f6eff2271 100644 --- a/spring-beans/src/main/java/org/springframework/beans/InvalidPropertyException.java +++ b/spring-beans/src/main/java/org/springframework/beans/InvalidPropertyException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,9 +28,9 @@ @SuppressWarnings("serial") public class InvalidPropertyException extends FatalBeanException { - private Class beanClass; + private final Class beanClass; - private String propertyName; + private final String propertyName; /** @@ -60,14 +60,14 @@ public InvalidPropertyException(Class beanClass, String propertyName, String * Return the offending bean class. */ public Class getBeanClass() { - return beanClass; + return this.beanClass; } /** * Return the name of the offending property. */ public String getPropertyName() { - return propertyName; + return this.propertyName; } } diff --git a/spring-beans/src/main/java/org/springframework/beans/MethodInvocationException.java b/spring-beans/src/main/java/org/springframework/beans/MethodInvocationException.java index e82daabbcb1a..c6e3e411ab86 100644 --- a/spring-beans/src/main/java/org/springframework/beans/MethodInvocationException.java +++ b/spring-beans/src/main/java/org/springframework/beans/MethodInvocationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author 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,7 +35,7 @@ public class MethodInvocationException extends PropertyAccessException { /** * Create a new MethodInvocationException. - * @param propertyChangeEvent PropertyChangeEvent that resulted in an exception + * @param propertyChangeEvent the PropertyChangeEvent that resulted in an exception * @param cause the Throwable raised by the invoked method */ public MethodInvocationException(PropertyChangeEvent propertyChangeEvent, Throwable cause) { 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 64593ad7fa39..3777ed0557cf 100644 --- a/spring-beans/src/main/java/org/springframework/beans/MutablePropertyValues.java +++ b/spring-beans/src/main/java/org/springframework/beans/MutablePropertyValues.java @@ -18,16 +18,21 @@ import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; /** - * Default implementation of the {@link PropertyValues} interface. + * The default implementation of the {@link PropertyValues} interface. * Allows simple manipulation of properties, and provides constructors * to support deep copy and construction from a Map. * @@ -80,7 +85,7 @@ public MutablePropertyValues(@Nullable PropertyValues original) { /** * Construct a new MutablePropertyValues object from a Map. - * @param original Map with property values keyed by property name Strings + * @param original a Map with property values keyed by property name Strings * @see #addPropertyValues(Map) */ public MutablePropertyValues(@Nullable Map original) { @@ -101,7 +106,7 @@ public MutablePropertyValues(@Nullable Map original) { * PropertyValue objects as-is. *

This is a constructor for advanced usage scenarios. * It is not intended for typical programmatic use. - * @param propertyValueList List of PropertyValue objects + * @param propertyValueList a List of PropertyValue objects */ public MutablePropertyValues(@Nullable List propertyValueList) { this.propertyValueList = @@ -145,7 +150,7 @@ public MutablePropertyValues addPropertyValues(@Nullable PropertyValues other) { /** * Add all property values from the given Map. - * @param other Map with property values keyed by property name, + * @param other a Map with property values keyed by property name, * which must be a String * @return this in order to allow for adding multiple property values in a chain */ @@ -160,7 +165,7 @@ public MutablePropertyValues addPropertyValues(@Nullable Map other) { /** * Add a PropertyValue object, replacing any existing one for the * corresponding property or getting merged with it (if applicable). - * @param pv PropertyValue object to add + * @param pv the PropertyValue object to add * @return this in order to allow for adding multiple property values in a chain */ public MutablePropertyValues addPropertyValue(PropertyValue pv) { @@ -244,6 +249,21 @@ public void removePropertyValue(String propertyName) { } + @Override + public Iterator iterator() { + return Collections.unmodifiableList(this.propertyValueList).iterator(); + } + + @Override + public Spliterator spliterator() { + return Spliterators.spliterator(this.propertyValueList, 0); + } + + @Override + public Stream stream() { + return this.propertyValueList.stream(); + } + @Override public PropertyValue[] getPropertyValues() { return this.propertyValueList.toArray(new PropertyValue[0]); @@ -348,14 +368,8 @@ public boolean isConverted() { @Override public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!(other instanceof MutablePropertyValues)) { - return false; - } - MutablePropertyValues that = (MutablePropertyValues) other; - return this.propertyValueList.equals(that.propertyValueList); + return (this == other || (other instanceof MutablePropertyValues && + this.propertyValueList.equals(((MutablePropertyValues) other).propertyValueList))); } @Override diff --git a/spring-beans/src/main/java/org/springframework/beans/NotWritablePropertyException.java b/spring-beans/src/main/java/org/springframework/beans/NotWritablePropertyException.java index 1399c69e6cf7..be43e1ac1e9d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/NotWritablePropertyException.java +++ b/spring-beans/src/main/java/org/springframework/beans/NotWritablePropertyException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,7 +30,7 @@ public class NotWritablePropertyException extends InvalidPropertyException { @Nullable - private String[] possibleMatches; + private final String[] possibleMatches; /** @@ -42,6 +42,7 @@ public NotWritablePropertyException(Class beanClass, String propertyName) { super(beanClass, propertyName, "Bean property '" + propertyName + "' is not writable or has an invalid setter method: " + "Does the return type of the getter match the parameter type of the setter?"); + this.possibleMatches = null; } /** @@ -52,6 +53,7 @@ public NotWritablePropertyException(Class beanClass, String propertyName) { */ public NotWritablePropertyException(Class beanClass, String propertyName, String msg) { super(beanClass, propertyName, msg); + this.possibleMatches = null; } /** @@ -63,6 +65,7 @@ public NotWritablePropertyException(Class beanClass, String propertyName, Str */ public NotWritablePropertyException(Class beanClass, String propertyName, String msg, Throwable cause) { super(beanClass, propertyName, msg, cause); + this.possibleMatches = null; } /** diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessException.java b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessException.java index bceb1c2e61d4..e2af5cafab7a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessException.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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 @@ public abstract class PropertyAccessException extends BeansException { @Nullable - private transient PropertyChangeEvent propertyChangeEvent; + private final PropertyChangeEvent propertyChangeEvent; /** @@ -52,6 +52,7 @@ public PropertyAccessException(PropertyChangeEvent propertyChangeEvent, String m */ public PropertyAccessException(String msg, @Nullable Throwable cause) { super(msg, cause); + this.propertyChangeEvent = null; } diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java index 960118126cdc..15d3be496b7b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,6 +39,11 @@ public interface PropertyAccessor { * Follows normal Java conventions: getFoo().getBar() would be "foo.bar". */ String NESTED_PROPERTY_SEPARATOR = "."; + + /** + * Path separator for nested properties. + * Follows normal Java conventions: getFoo().getBar() would be "foo.bar". + */ char NESTED_PROPERTY_SEPARATOR_CHAR = '.'; /** @@ -46,6 +51,11 @@ public interface PropertyAccessor { * indexed or mapped property like "person.addresses[0]". */ String PROPERTY_KEY_PREFIX = "["; + + /** + * Marker that indicates the start of a property key for an + * indexed or mapped property like "person.addresses[0]". + */ char PROPERTY_KEY_PREFIX_CHAR = '['; /** @@ -53,6 +63,11 @@ public interface PropertyAccessor { * indexed or mapped property like "person.addresses[0]". */ String PROPERTY_KEY_SUFFIX = "]"; + + /** + * Marker that indicates the end of a property key for an + * indexed or mapped property like "person.addresses[0]". + */ char PROPERTY_KEY_SUFFIX_CHAR = ']'; @@ -141,7 +156,7 @@ public interface PropertyAccessor { *

Bulk updates from PropertyValues are more powerful: This method is * provided for convenience. Behavior will be identical to that of * the {@link #setPropertyValues(PropertyValues)} method. - * @param map Map to take properties from. Contains property value objects, + * @param map a Map to take properties from. Contains property value objects, * keyed by property name * @throws InvalidPropertyException if there is no such property or * if the property isn't writable @@ -162,7 +177,7 @@ public interface PropertyAccessor { * This exception can be examined later to see all binding errors. * Properties that were successfully updated remain changed. *

Does not allow unknown fields or invalid fields. - * @param pvs PropertyValues to set on the target object + * @param pvs a PropertyValues to set on the target object * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions @@ -182,7 +197,7 @@ public interface PropertyAccessor { * {@link PropertyBatchUpdateException} containing all the individual errors. * This exception can be examined later to see all binding errors. * Properties that were successfully updated remain changed. - * @param pvs PropertyValues to set on the target object + * @param pvs a PropertyValues to set on the target object * @param ignoreUnknown should we ignore unknown properties (not found in the bean) * @throws InvalidPropertyException if there is no such property or * if the property isn't writable @@ -204,7 +219,7 @@ void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown) * {@link PropertyBatchUpdateException} containing all the individual errors. * This exception can be examined later to see all binding errors. * Properties that were successfully updated remain changed. - * @param pvs PropertyValues to set on the target object + * @param pvs a PropertyValues to set on the target object * @param ignoreUnknown should we ignore unknown properties (not found in the bean) * @param ignoreInvalid should we ignore invalid properties (found but not accessible) * @throws InvalidPropertyException if there is no such property or diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessorFactory.java b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessorFactory.java index f605806983e2..d33a96ea031b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessorFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2018 the original author 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,11 @@ * @author Juergen Hoeller * @since 2.5.2 */ -public abstract class PropertyAccessorFactory { +public final class PropertyAccessorFactory { + + private PropertyAccessorFactory() { + } + /** * Obtain a BeanWrapper for the given target object, diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyBatchUpdateException.java b/spring-beans/src/main/java/org/springframework/beans/PropertyBatchUpdateException.java index b3bdfeabdd38..c5030c728a8f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyBatchUpdateException.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyBatchUpdateException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,8 +39,8 @@ @SuppressWarnings("serial") public class PropertyBatchUpdateException extends BeansException { - /** List of PropertyAccessException objects */ - private PropertyAccessException[] propertyAccessExceptions; + /** List of PropertyAccessException objects. */ + private final PropertyAccessException[] propertyAccessExceptions; /** 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 773d06542d5b..c549c9c47573 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-2017 the original author or authors. + * Copyright 2002-2018 the original author 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 @@ * @author Chris Beams * @author Juergen Hoeller */ -class PropertyDescriptorUtils { +abstract class PropertyDescriptorUtils { /** * See {@link java.beans.FeatureDescriptor}. */ - public static void copyNonMethodProperties(PropertyDescriptor source, PropertyDescriptor target) - throws IntrospectionException { - + public static void copyNonMethodProperties(PropertyDescriptor source, PropertyDescriptor target) { target.setExpert(source.isExpert()); target.setHidden(source.isHidden()); target.setPreferred(source.isPreferred()); 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 4f2528d60199..c6ab36971f83 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java @@ -27,12 +27,12 @@ import java.nio.charset.Charset; import java.nio.file.Path; import java.time.ZoneId; +import java.util.ArrayList; import java.util.Collection; import java.util.Currency; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; -import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -318,7 +318,7 @@ public PropertyEditor findCustomEditor(@Nullable Class requiredType, @Nullabl // Check property-specific editor first. PropertyEditor editor = getCustomEditor(propertyPath, requiredType); if (editor == null) { - List strippedPaths = new LinkedList<>(); + List strippedPaths = new ArrayList<>(); addStrippedPropertyPaths(strippedPaths, "", propertyPath); for (Iterator it = strippedPaths.iterator(); it.hasNext() && editor == null;) { String strippedPath = it.next(); @@ -438,7 +438,7 @@ protected Class guessPropertyTypeFromEditors(String propertyName) { if (this.customEditorsForPath != null) { CustomEditorHolder editorHolder = this.customEditorsForPath.get(propertyName); if (editorHolder == null) { - List strippedPaths = new LinkedList<>(); + List strippedPaths = new ArrayList<>(); addStrippedPropertyPaths(strippedPaths, "", propertyName); for (Iterator it = strippedPaths.iterator(); it.hasNext() && editorHolder == null;) { String strippedName = it.next(); @@ -517,7 +517,7 @@ private void addStrippedPropertyPaths(List strippedPaths, String nestedP * Holder for a registered custom editor with property name. * Keeps the PropertyEditor itself plus the type it was registered for. */ - private static class CustomEditorHolder { + private static final class CustomEditorHolder { private final PropertyEditor propertyEditor; diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyMatches.java b/spring-beans/src/main/java/org/springframework/beans/PropertyMatches.java index f37275ba18ce..1c41ea9ee37f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyMatches.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyMatches.java @@ -42,7 +42,7 @@ */ public abstract class PropertyMatches { - /** Default maximum property distance: 2 */ + /** Default maximum property distance: 2. */ public static final int DEFAULT_MAX_DISTANCE = 2; diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java b/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java index 1f1536fc1b9d..11086f542540 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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 class PropertyValue extends BeanMetadataAttributeAccessor implements Seri @Nullable private Object convertedValue; - /** Package-visible field that indicates whether conversion is necessary */ + /** Package-visible field that indicates whether conversion is necessary. */ @Nullable volatile Boolean conversionNecessary; - /** Package-visible field for caching the resolved property path tokens */ + /** Package-visible field for caching the resolved property path tokens. */ @Nullable transient volatile Object resolvedTokens; 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 786354aeb83c..94e3031ff657 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyValues.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyValues.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,13 @@ package org.springframework.beans; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + import org.springframework.lang.Nullable; /** @@ -27,7 +34,33 @@ * @since 13 May 2001 * @see PropertyValue */ -public interface PropertyValues { +public interface PropertyValues extends Iterable { + + /** + * Return an {@link Iterator} over the property values. + * @since 5.1 + */ + @Override + default Iterator iterator() { + return Arrays.asList(getPropertyValues()).iterator(); + } + + /** + * Return a {@link Spliterator} over the property values. + * @since 5.1 + */ + @Override + default Spliterator spliterator() { + return Spliterators.spliterator(getPropertyValues(), 0); + } + + /** + * Return a sequential {@link Stream} containing the property values. + * @since 5.1 + */ + default Stream stream() { + return StreamSupport.stream(spliterator(), false); + } /** * Return an array of the PropertyValue objects held in this object. @@ -46,7 +79,7 @@ public interface PropertyValues { * Return the changes since the previous PropertyValues. * Subclasses should also override {@code equals}. * @param old old property values - * @return PropertyValues updated or new properties. + * @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/TypeConverter.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverter.java index 5e38a1e93e32..8b8b8dedfc24 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeConverter.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverter.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. @@ -19,6 +19,7 @@ import java.lang.reflect.Field; import org.springframework.core.MethodParameter; +import org.springframework.core.convert.TypeDescriptor; import org.springframework.lang.Nullable; /** @@ -93,4 +94,27 @@ T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType, @Nullable Field field) throws TypeMismatchException; + /** + * Convert the value to the required type (if necessary from a String). + *

Conversions from String to any type will typically use the {@code setAsText} + * method of the PropertyEditor class, or a Spring Converter in a ConversionService. + * @param value the value to convert + * @param requiredType the type we must convert to + * (or {@code null} if not known, for example in case of a collection element) + * @param typeDescriptor the type descriptor to use (may be {@code null})) + * @return the new value, possibly the result of type conversion + * @throws TypeMismatchException if type conversion failed + * @since 5.1.4 + * @see java.beans.PropertyEditor#setAsText(String) + * @see java.beans.PropertyEditor#getValue() + * @see org.springframework.core.convert.ConversionService + * @see org.springframework.core.convert.converter.Converter + */ + @Nullable + default T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType, + @Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException { + + throw new UnsupportedOperationException("TypeDescriptor resolution not supported"); + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java index 11c2aab641b2..343ba8c42d1e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.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. @@ -30,7 +30,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.core.CollectionFactory; -import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; @@ -82,42 +81,6 @@ public TypeConverterDelegate(PropertyEditorRegistrySupport propertyEditorRegistr } - /** - * Convert the value to the specified required type. - * @param newValue the proposed new value - * @param requiredType the type we must convert to - * (or {@code null} if not known, for example in case of a collection element) - * @param methodParam the method parameter that is the target of the conversion - * (may be {@code null}) - * @return the new value, possibly the result of type conversion - * @throws IllegalArgumentException if type conversion failed - */ - @Nullable - public T convertIfNecessary(@Nullable Object newValue, @Nullable Class requiredType, - @Nullable MethodParameter methodParam) throws IllegalArgumentException { - - return convertIfNecessary(null, null, newValue, requiredType, - (methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType))); - } - - /** - * Convert the value to the specified required type. - * @param newValue the proposed new value - * @param requiredType the type we must convert to - * (or {@code null} if not known, for example in case of a collection element) - * @param field the reflective field that is the target of the conversion - * (may be {@code null}) - * @return the new value, possibly the result of type conversion - * @throws IllegalArgumentException if type conversion failed - */ - @Nullable - public T convertIfNecessary(@Nullable Object newValue, @Nullable Class requiredType, @Nullable Field field) - throws IllegalArgumentException { - - return convertIfNecessary(null, null, newValue, requiredType, - (field != null ? new TypeDescriptor(field) : TypeDescriptor.valueOf(requiredType))); - } - /** * Convert the value to the required type for the specified property. * @param propertyName name of the property @@ -247,7 +210,7 @@ else if (convertedValue instanceof String && !requiredType.isInstance(convertedV } } String trimmedValue = ((String) convertedValue).trim(); - if (requiredType.isEnum() && "".equals(trimmedValue)) { + if (requiredType.isEnum() && trimmedValue.isEmpty()) { // It's an empty enum identifier: reset the enum value to null. return null; } diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java index 809cd61250b9..bf1a794d4659 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.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. @@ -21,6 +21,7 @@ import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionException; import org.springframework.core.convert.ConverterNotFoundException; +import org.springframework.core.convert.TypeDescriptor; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -41,15 +42,16 @@ public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport @Override @Nullable public T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType) throws TypeMismatchException { - return doConvert(value, requiredType, null, null); + return convertIfNecessary(value, requiredType, TypeDescriptor.valueOf(requiredType)); } @Override @Nullable - public T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType, @Nullable MethodParameter methodParam) - throws TypeMismatchException { + public T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType, + @Nullable MethodParameter methodParam) throws TypeMismatchException { - return doConvert(value, requiredType, methodParam, null); + return convertIfNecessary(value, requiredType, + (methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType))); } @Override @@ -57,21 +59,18 @@ public T convertIfNecessary(@Nullable Object value, @Nullable Class requi public T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType, @Nullable Field field) throws TypeMismatchException { - return doConvert(value, requiredType, null, field); + return convertIfNecessary(value, requiredType, + (field != null ? new TypeDescriptor(field) : TypeDescriptor.valueOf(requiredType))); } @Nullable - private T doConvert(@Nullable Object value,@Nullable Class requiredType, - @Nullable MethodParameter methodParam, @Nullable Field field) throws TypeMismatchException { + @Override + public T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType, + @Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException { Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate"); try { - if (field != null) { - return this.typeConverterDelegate.convertIfNecessary(value, requiredType, field); - } - else { - return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam); - } + return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType, typeDescriptor); } catch (ConverterNotFoundException | IllegalStateException ex) { throw new ConversionNotSupportedException(value, requiredType, ex); 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 487319d6b8f4..77670f3811de 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeMismatchException.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeMismatchException.java @@ -69,9 +69,9 @@ public TypeMismatchException(PropertyChangeEvent propertyChangeEvent, @Nullable "Failed to convert property value of type '" + ClassUtils.getDescriptiveType(propertyChangeEvent.getNewValue()) + "'" + (requiredType != null ? - " to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : "") + + " to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : "") + (propertyChangeEvent.getPropertyName() != null ? - " for property '" + propertyChangeEvent.getPropertyName() + "'" : ""), + " for property '" + propertyChangeEvent.getPropertyName() + "'" : ""), cause); this.propertyName = propertyChangeEvent.getPropertyName(); this.value = propertyChangeEvent.getNewValue(); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/Aware.java b/spring-beans/src/main/java/org/springframework/beans/factory/Aware.java index f993f1f7135a..8423a458e169 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/Aware.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/Aware.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2018 the original author 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,21 +17,19 @@ package org.springframework.beans.factory; /** - * Marker superinterface indicating that a bean is eligible to be - * notified by the Spring container of a particular framework object - * through a callback-style method. Actual method signature is - * determined by individual subinterfaces, but should typically - * consist of just one void-returning method that accepts a single - * argument. + * A marker superinterface indicating that a bean is eligible to be notified by the + * Spring container of a particular framework object through a callback-style method. + * The actual method signature is determined by individual subinterfaces but should + * typically consist of just one void-returning method that accepts a single argument. * - *

Note that merely implementing {@link Aware} provides no default - * functionality. Rather, processing must be done explicitly, for example - * in a {@link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessor}. + *

Note that merely implementing {@link Aware} provides no default functionality. + * Rather, processing must be done explicitly, for example in a + * {@link org.springframework.beans.factory.config.BeanPostProcessor}. * Refer to {@link org.springframework.context.support.ApplicationContextAwareProcessor} - * and {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory} - * for examples of processing {@code *Aware} interface callbacks. + * for an example of processing specific {@code *Aware} interface callbacks. * * @author Chris Beams + * @author Juergen Hoeller * @since 3.1 */ public interface Aware { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java index cb968c736b9c..677ca28022ac 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java @@ -18,7 +18,7 @@ import java.io.PrintStream; import java.io.PrintWriter; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import org.springframework.beans.FatalBeanException; @@ -35,10 +35,10 @@ public class BeanCreationException extends FatalBeanException { @Nullable - private String beanName; + private final String beanName; @Nullable - private String resourceDescription; + private final String resourceDescription; @Nullable private List relatedCauses; @@ -50,6 +50,8 @@ public class BeanCreationException extends FatalBeanException { */ public BeanCreationException(String msg) { super(msg); + this.beanName = null; + this.resourceDescription = null; } /** @@ -59,6 +61,8 @@ public BeanCreationException(String msg) { */ public BeanCreationException(String msg, Throwable cause) { super(msg, cause); + this.beanName = null; + this.resourceDescription = null; } /** @@ -69,6 +73,7 @@ public BeanCreationException(String msg, Throwable cause) { public BeanCreationException(String beanName, String msg) { super("Error creating bean with name '" + beanName + "': " + msg); this.beanName = beanName; + this.resourceDescription = null; } /** @@ -94,6 +99,7 @@ public BeanCreationException(@Nullable String resourceDescription, @Nullable Str (resourceDescription != null ? " defined in " + resourceDescription : "") + ": " + msg); this.resourceDescription = resourceDescription; this.beanName = beanName; + this.relatedCauses = null; } /** @@ -135,7 +141,7 @@ public String getBeanName() { */ public void addRelatedCause(Throwable ex) { if (this.relatedCauses == null) { - this.relatedCauses = new LinkedList<>(); + this.relatedCauses = new ArrayList<>(); } this.relatedCauses.add(ex); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanDefinitionStoreException.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanDefinitionStoreException.java index 44c23a3de831..f89b59adb811 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanDefinitionStoreException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanDefinitionStoreException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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 @@ public class BeanDefinitionStoreException extends FatalBeanException { @Nullable - private String resourceDescription; + private final String resourceDescription; @Nullable - private String beanName; + private final String beanName; /** @@ -43,6 +43,8 @@ public class BeanDefinitionStoreException extends FatalBeanException { */ public BeanDefinitionStoreException(String msg) { super(msg); + this.resourceDescription = null; + this.beanName = null; } /** @@ -52,6 +54,8 @@ public BeanDefinitionStoreException(String msg) { */ public BeanDefinitionStoreException(String msg, @Nullable Throwable cause) { super(msg, cause); + this.resourceDescription = null; + this.beanName = null; } /** @@ -62,6 +66,7 @@ public BeanDefinitionStoreException(String msg, @Nullable Throwable cause) { public BeanDefinitionStoreException(@Nullable String resourceDescription, String msg) { super(msg); this.resourceDescription = resourceDescription; + this.beanName = null; } /** @@ -73,12 +78,13 @@ public BeanDefinitionStoreException(@Nullable String resourceDescription, String public BeanDefinitionStoreException(@Nullable String resourceDescription, String msg, @Nullable Throwable cause) { super(msg, cause); this.resourceDescription = resourceDescription; + this.beanName = null; } /** * Create a new BeanDefinitionStoreException. * @param resourceDescription description of the resource that the bean definition came from - * @param beanName the name of the bean requested + * @param beanName the name of the bean * @param msg the detail message (appended to an introductory message that indicates * the resource and the name of the bean) */ @@ -89,21 +95,23 @@ public BeanDefinitionStoreException(@Nullable String resourceDescription, String /** * Create a new BeanDefinitionStoreException. * @param resourceDescription description of the resource that the bean definition came from - * @param beanName the name of the bean requested + * @param beanName the name of the bean * @param msg the detail message (appended to an introductory message that indicates * the resource and the name of the bean) * @param cause the root cause (may be {@code null}) */ - public BeanDefinitionStoreException(@Nullable String resourceDescription, String beanName, String msg, @Nullable Throwable cause) { - super("Invalid bean definition with name '" + beanName + "' defined in " + resourceDescription + ": " + msg, cause); + public BeanDefinitionStoreException( + @Nullable String resourceDescription, String beanName, String msg, @Nullable Throwable cause) { + + super("Invalid bean definition with name '" + beanName + "' defined in " + resourceDescription + ": " + msg, + cause); this.resourceDescription = resourceDescription; this.beanName = beanName; } /** - * Return the description of the resource that the bean - * definition came from, if any. + * Return the description of the resource that the bean definition came from, if available. */ @Nullable public String getResourceDescription() { @@ -111,7 +119,7 @@ public String getResourceDescription() { } /** - * Return the name of the bean requested, if any. + * Return the name of the bean, if available. */ @Nullable public String getBeanName() { 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 d3bfa88f664b..a0d64955f565 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-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. @@ -133,8 +133,7 @@ public interface BeanFactory { * 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 definition - * with the specified name + * @throws NoSuchBeanDefinitionException if there is no bean with the specified name * @throws BeansException if the bean could not be obtained */ Object getBean(String name) throws BeansException; @@ -148,16 +147,13 @@ public interface BeanFactory { *

Translates aliases back to the corresponding canonical bean name. * 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 - * of the actual class, or {@code null} for any match. For example, if the value - * is {@code Object.class}, this method will succeed whatever the class of the - * returned instance. + * @param requiredType type the bean must match; can be an interface or superclass * @return an instance of the bean * @throws NoSuchBeanDefinitionException if there is no such bean definition * @throws BeanNotOfRequiredTypeException if the bean is not of the required type * @throws BeansException if the bean could not be created */ - T getBean(String name, @Nullable Class requiredType) throws BeansException; + T getBean(String name, Class requiredType) throws BeansException; /** * Return an instance, which may be shared or independent, of the specified bean. @@ -181,8 +177,7 @@ public interface BeanFactory { * but may also be translated into a conventional by-name lookup based on the name * of the given type. For more extensive retrieval operations across sets of beans, * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}. - * @param requiredType type the bean must match; can be an interface or superclass. - * {@code null} is disallowed. + * @param requiredType type the bean must match; can be an interface or superclass * @return an instance of the single bean matching the required type * @throws NoSuchBeanDefinitionException if no bean of the given type was found * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found @@ -200,8 +195,7 @@ public interface BeanFactory { * but may also be translated into a conventional by-name lookup based on the name * of the given type. For more extensive retrieval operations across sets of beans, * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}. - * @param requiredType type the bean must match; can be an interface or superclass. - * {@code null} is disallowed. + * @param requiredType type the bean must match; can be an interface or superclass * @param args arguments to use when creating a bean instance using explicit arguments * (only applied when creating a new instance as opposed to retrieving an existing one) * @return an instance of the bean @@ -213,6 +207,31 @@ public interface BeanFactory { */ T getBean(Class requiredType, Object... args) throws BeansException; + /** + * Return an 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 + * @return a corresponding provider handle + * @since 5.1 + * @see #getBeanProvider(ResolvableType) + */ + ObjectProvider getBeanProvider(Class requiredType); + + /** + * Return an 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. + * @return a corresponding provider handle + * @since 5.1 + * @see ObjectProvider#iterator() + * @see ObjectProvider#stream() + * @see ObjectProvider#orderedStream() + */ + ObjectProvider getBeanProvider(ResolvableType requiredType); /** * Does this bean factory contain a bean definition or externally registered singleton @@ -298,7 +317,7 @@ public interface BeanFactory { * @see #getBean * @see #getType */ - boolean isTypeMatch(String name, @Nullable Class typeToMatch) throws NoSuchBeanDefinitionException; + boolean isTypeMatch(String name, Class typeToMatch) throws NoSuchBeanDefinitionException; /** * Determine the type of the bean with the given name. More specifically, diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java index 2c37b5dfa65e..28b96e1d842f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java @@ -22,6 +22,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import org.springframework.beans.BeansException; import org.springframework.core.ResolvableType; @@ -51,6 +52,13 @@ public abstract class BeanFactoryUtils { */ public static final String GENERATED_BEAN_NAME_SEPARATOR = "#"; + /** + * Cache from name with factory bean prefix to stripped name without dereference. + * @since 5.1 + * @see BeanFactory#FACTORY_BEAN_PREFIX + */ + private static final Map transformedBeanNameCache = new ConcurrentHashMap<>(); + /** * Return whether the given name is a factory dereference @@ -72,11 +80,16 @@ public static boolean isFactoryDereference(@Nullable String name) { */ public static String transformedBeanName(String name) { Assert.notNull(name, "'name' must not be null"); - String beanName = name; - while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { - beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); + if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { + return name; } - return beanName; + return transformedBeanNameCache.computeIfAbsent(name, beanName -> { + do { + beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); + } + while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)); + return beanName; + }); } /** @@ -106,6 +119,8 @@ public static String originalBeanName(String name) { } + // Retrieval of bean names + /** * Count all beans in any hierarchy in which this factory participates. * Includes counts of ancestor bean factories. @@ -113,6 +128,7 @@ public static String originalBeanName(String name) { * with the same name) are only counted once. * @param lbf the bean factory * @return count of beans including those defined in ancestor factories + * @see #beanNamesIncludingAncestors */ public static int countBeansIncludingAncestors(ListableBeanFactory lbf) { return beanNamesIncludingAncestors(lbf).length; @@ -140,6 +156,7 @@ public static String[] beanNamesIncludingAncestors(ListableBeanFactory lbf) { * @param type the type that beans must match (as a {@code ResolvableType}) * @return the array of matching bean names, or an empty array if none * @since 4.2 + * @see ListableBeanFactory#getBeanNamesForType(ResolvableType) */ public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, ResolvableType type) { Assert.notNull(lbf, "ListableBeanFactory must not be null"); @@ -166,6 +183,7 @@ public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lb * @param lbf the bean factory * @param type the type that beans must match (as a {@code Class}) * @return the array of matching bean names, or an empty array if none + * @see ListableBeanFactory#getBeanNamesForType(Class) */ public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, Class type) { Assert.notNull(lbf, "ListableBeanFactory must not be null"); @@ -200,6 +218,7 @@ public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lb * for this flag will initialize FactoryBeans and "factory-bean" references. * @param type the type that beans must match * @return the array of matching bean names, or an empty array if none + * @see ListableBeanFactory#getBeanNamesForType(Class, boolean, boolean) */ public static String[] beanNamesForTypeIncludingAncestors( ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit) { @@ -217,6 +236,35 @@ public static String[] beanNamesForTypeIncludingAncestors( return result; } + /** + * Get all bean names whose {@code Class} has the supplied {@link Annotation} + * type, including those defined in ancestor factories, without creating any bean + * instances yet. Will return unique names in case of overridden bean definitions. + * @param lbf the bean factory + * @param annotationType the type of annotation to look for + * @return the array of matching bean names, or an empty array if none + * @since 5.0 + * @see ListableBeanFactory#getBeanNamesForAnnotation(Class) + */ + public static String[] beanNamesForAnnotationIncludingAncestors( + ListableBeanFactory lbf, Class annotationType) { + + Assert.notNull(lbf, "ListableBeanFactory must not be null"); + String[] result = lbf.getBeanNamesForAnnotation(annotationType); + if (lbf instanceof HierarchicalBeanFactory) { + HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf; + if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) { + String[] parentResult = beanNamesForAnnotationIncludingAncestors( + (ListableBeanFactory) hbf.getParentBeanFactory(), annotationType); + result = mergeNamesWithParent(result, parentResult, hbf); + } + } + return result; + } + + + // Retrieval of bean instances + /** * Return all beans of the given type or subtypes, also picking up beans defined in * ancestor bean factories if the current bean factory is a HierarchicalBeanFactory. @@ -233,6 +281,7 @@ public static String[] beanNamesForTypeIncludingAncestors( * @param type type of bean to match * @return the Map of matching bean instances, or an empty Map if none * @throws BeansException if a bean could not be created + * @see ListableBeanFactory#getBeansOfType(Class) */ public static Map beansOfTypeIncludingAncestors(ListableBeanFactory lbf, Class type) throws BeansException { @@ -245,9 +294,9 @@ public static Map beansOfTypeIncludingAncestors(ListableBeanFacto if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) { Map parentResult = beansOfTypeIncludingAncestors( (ListableBeanFactory) hbf.getParentBeanFactory(), type); - parentResult.forEach((beanName, beanType) -> { + parentResult.forEach((beanName, beanInstance) -> { if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) { - result.put(beanName, beanType); + result.put(beanName, beanInstance); } }); } @@ -280,6 +329,7 @@ public static Map beansOfTypeIncludingAncestors(ListableBeanFacto * for this flag will initialize FactoryBeans and "factory-bean" references. * @return the Map of matching bean instances, or an empty Map if none * @throws BeansException if a bean could not be created + * @see ListableBeanFactory#getBeansOfType(Class, boolean, boolean) */ public static Map beansOfTypeIncludingAncestors( ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit) @@ -293,9 +343,9 @@ public static Map beansOfTypeIncludingAncestors( if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) { Map parentResult = beansOfTypeIncludingAncestors( (ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit); - parentResult.forEach((beanName, beanType) -> { + parentResult.forEach((beanName, beanInstance) -> { if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) { - result.put(beanName, beanType); + result.put(beanName, beanInstance); } }); } @@ -303,7 +353,6 @@ public static Map beansOfTypeIncludingAncestors( return result; } - /** * Return a single bean of the given type or subtypes, also picking up beans * defined in ancestor bean factories if the current bean factory is a @@ -325,6 +374,7 @@ public static Map beansOfTypeIncludingAncestors( * @throws NoSuchBeanDefinitionException if no bean of the given type was found * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @throws BeansException if the bean could not be created + * @see #beansOfTypeIncludingAncestors(ListableBeanFactory, Class) */ public static T beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class type) throws BeansException { @@ -333,31 +383,6 @@ public static T beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class< return uniqueBean(type, beansOfType); } - /** - * Get all bean names whose {@code Class} has the supplied {@link Annotation} - * type, including those defined in ancestor factories, without creating any bean - * instances yet. Will return unique names in case of overridden bean definitions. - * @param lbf the bean factory - * @param annotationType the type of annotation to look for - * @return the array of matching bean names, or an empty array if none - * @since 5.0 - */ - public static String[] beanNamesForAnnotationIncludingAncestors( - ListableBeanFactory lbf, Class annotationType) { - - Assert.notNull(lbf, "ListableBeanFactory must not be null"); - String[] result = lbf.getBeanNamesForAnnotation(annotationType); - if (lbf instanceof HierarchicalBeanFactory) { - HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf; - if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) { - String[] parentResult = beanNamesForAnnotationIncludingAncestors( - (ListableBeanFactory) hbf.getParentBeanFactory(), annotationType); - result = mergeNamesWithParent(result, parentResult, hbf); - } - } - return result; - } - /** * Return a single bean of the given type or subtypes, also picking up beans * defined in ancestor bean factories if the current bean factory is a @@ -386,6 +411,7 @@ public static String[] beanNamesForAnnotationIncludingAncestors( * @throws NoSuchBeanDefinitionException if no bean of the given type was found * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @throws BeansException if the bean could not be created + * @see #beansOfTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean) */ public static T beanOfTypeIncludingAncestors( ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit) @@ -410,6 +436,7 @@ public static T beanOfTypeIncludingAncestors( * @throws NoSuchBeanDefinitionException if no bean of the given type was found * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @throws BeansException if the bean could not be created + * @see ListableBeanFactory#getBeansOfType(Class) */ public static T beanOfType(ListableBeanFactory lbf, Class type) throws BeansException { Assert.notNull(lbf, "ListableBeanFactory must not be null"); @@ -440,6 +467,7 @@ public static T beanOfType(ListableBeanFactory lbf, Class type) throws Be * @throws NoSuchBeanDefinitionException if no bean of the given type was found * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @throws BeansException if the bean could not be created + * @see ListableBeanFactory#getBeansOfType(Class, boolean, boolean) */ public static T beanOfType( ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java index 1cb0b75830eb..6b07af29a4b7 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author 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,14 @@ @SuppressWarnings("serial") public class BeanNotOfRequiredTypeException extends BeansException { - /** The name of the instance that was of the wrong type */ - private String beanName; + /** The name of the instance that was of the wrong type. */ + private final String beanName; - /** The required type */ - private Class requiredType; + /** The required type. */ + private final Class requiredType; - /** The offending type */ - private Class actualType; + /** The offending type. */ + private final Class actualType; /** diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/CannotLoadBeanClassException.java b/spring-beans/src/main/java/org/springframework/beans/factory/CannotLoadBeanClassException.java index f2a928947df6..e8a3dd108054 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/CannotLoadBeanClassException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/CannotLoadBeanClassException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,12 @@ public class CannotLoadBeanClassException extends FatalBeanException { @Nullable - private String resourceDescription; + private final String resourceDescription; - @Nullable - private String beanName; + private final String beanName; @Nullable - private String beanClassName; + private final String beanClassName; /** @@ -47,8 +46,8 @@ public class CannotLoadBeanClassException extends FatalBeanException { * @param beanClassName the name of the bean class * @param cause the root cause */ - public CannotLoadBeanClassException( - @Nullable String resourceDescription, String beanName, @Nullable String beanClassName, ClassNotFoundException cause) { + public CannotLoadBeanClassException(@Nullable String resourceDescription, String beanName, + @Nullable String beanClassName, ClassNotFoundException cause) { super("Cannot find class [" + beanClassName + "] for bean with name '" + beanName + "'" + (resourceDescription != null ? " defined in " + resourceDescription : ""), cause); @@ -65,8 +64,8 @@ public CannotLoadBeanClassException( * @param beanClassName the name of the bean class * @param cause the root cause */ - public CannotLoadBeanClassException( - @Nullable String resourceDescription, String beanName, @Nullable String beanClassName, LinkageError cause) { + public CannotLoadBeanClassException(@Nullable String resourceDescription, String beanName, + @Nullable String beanClassName, LinkageError cause) { super("Error loading class [" + beanClassName + "] for bean with name '" + beanName + "'" + (resourceDescription != null ? " defined in " + resourceDescription : "") + @@ -89,7 +88,6 @@ public String getResourceDescription() { /** * Return the name of the bean requested. */ - @Nullable public String getBeanName() { return this.beanName; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/DisposableBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/DisposableBean.java index 7c5b49578d91..d92f7ed6a2f8 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/DisposableBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/DisposableBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author 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,28 +17,29 @@ package org.springframework.beans.factory; /** - * Interface to be implemented by beans that want to release resources - * on destruction. A BeanFactory is supposed to invoke the destroy - * method if it disposes a cached singleton. An application context - * is supposed to dispose all of its singletons on close. + * Interface to be implemented by beans that want to release resources on destruction. + * A {@link BeanFactory} will invoke the destroy method on individual destruction of a + * scoped bean. An {@link org.springframework.context.ApplicationContext} is supposed + * to dispose all of its singletons on shutdown, driven by the application lifecycle. * - *

An alternative to implementing DisposableBean is specifying a custom - * destroy-method, for example in an XML bean definition. - * For a list of all bean lifecycle methods, see the - * {@link BeanFactory BeanFactory javadocs}. + *

A Spring-managed bean may also implement Java's {@link AutoCloseable} interface + * for the same purpose. An alternative to implementing an interface is specifying a + * custom destroy method, for example in an XML bean definition. For a list of all + * bean lifecycle methods, see the {@link BeanFactory BeanFactory javadocs}. * * @author Juergen Hoeller * @since 12.08.2003 - * @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName - * @see org.springframework.context.ConfigurableApplicationContext#close + * @see InitializingBean + * @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName() + * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#destroySingletons() + * @see org.springframework.context.ConfigurableApplicationContext#close() */ public interface DisposableBean { /** - * Invoked by a BeanFactory on destruction of a singleton. - * @throws Exception in case of shutdown errors. - * Exceptions will get logged but not rethrown to allow - * other beans to release their resources too. + * Invoked by the containing {@code BeanFactory} on destruction of a bean. + * @throws Exception in case of shutdown errors. Exceptions will get logged + * but not rethrown to allow other beans to release their resources as well. */ void destroy() throws Exception; 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 78c008c29778..4746ada38181 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-2016 the original author or authors. + * Copyright 2002-2018 the original author 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,6 +51,7 @@ * @author Rod Johnson * @author Juergen Hoeller * @since 08.03.2003 + * @param the bean type * @see org.springframework.beans.factory.BeanFactory * @see org.springframework.aop.framework.ProxyFactoryBean * @see org.springframework.jndi.JndiObjectFactoryBean diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/InitializingBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/InitializingBean.java index 9676ff3433a9..b0ca094510e3 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/InitializingBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/InitializingBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author 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,32 +17,29 @@ package org.springframework.beans.factory; /** - * Interface to be implemented by beans that need to react once all their - * properties have been set by a BeanFactory: for example, to perform custom - * initialization, or merely to check that all mandatory properties have been set. + * Interface to be implemented by beans that need to react once all their properties + * have been set by a {@link BeanFactory}: e.g. to perform custom initialization, + * or merely to check that all mandatory properties have been set. * - *

An alternative to implementing InitializingBean is specifying a custom - * init-method, for example in an XML bean definition. - * For a list of all bean lifecycle methods, see the - * {@link BeanFactory BeanFactory javadocs}. + *

An alternative to implementing {@code InitializingBean} is specifying a custom + * init method, for example in an XML bean definition. For a list of all bean + * lifecycle methods, see the {@link BeanFactory BeanFactory javadocs}. * * @author Rod Johnson - * @see BeanNameAware - * @see BeanFactoryAware - * @see BeanFactory - * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName - * @see org.springframework.context.ApplicationContextAware + * @author Juergen Hoeller + * @see DisposableBean + * @see org.springframework.beans.factory.config.BeanDefinition#getPropertyValues() + * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getInitMethodName() */ public interface InitializingBean { /** - * Invoked by a BeanFactory after it has set all bean properties supplied - * (and satisfied BeanFactoryAware and ApplicationContextAware). - *

This method allows the bean instance to perform initialization only - * possible when all bean properties have been set and to throw an - * exception in the event of misconfiguration. - * @throws Exception in the event of misconfiguration (such - * as failure to set an essential property) or if initialization fails. + * Invoked by the containing {@code BeanFactory} after it has set all bean properties + * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc. + *

This method allows the bean instance to perform validation of its overall + * configuration and final initialization when all bean properties have been set. + * @throws Exception in the event of misconfiguration (such as failure to set an + * essential property) or if initialization fails for any other reason */ void afterPropertiesSet() throws Exception; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java b/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java index 4a6eda2bbfaa..cd0812d1875b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.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. @@ -29,6 +29,8 @@ /** * A simple descriptor for an injection point, pointing to a method/constructor * parameter or a field. Exposed by {@link UnsatisfiedDependencyException}. + * Also available as an argument for factory methods, reacting to the + * requesting injection point for building a customized bean instance. * * @author Juergen Hoeller * @since 4.3 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 e2b841f0dacc..b44c32ddeb9b 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-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -244,7 +244,9 @@ Map getBeansOfType(@Nullable Class type, boolean includeNonSin /** * Find all names of beans whose {@code Class} has the supplied {@link Annotation} - * type, without creating any bean instances yet. + * type, without creating corresponding bean instances yet. + *

Note that this method considers objects created by FactoryBeans, which means + * that FactoryBeans will get initialized in order to determine their object type. * @param annotationType the type of annotation to look for * @return the names of all matching beans * @since 4.0 @@ -254,6 +256,8 @@ Map getBeansOfType(@Nullable Class type, boolean includeNonSin /** * Find all beans whose {@code Class} has the supplied {@link Annotation} type, * returning a Map of bean names with corresponding bean instances. + *

Note that this method considers objects created by FactoryBeans, which means + * that FactoryBeans will get initialized in order to determine their object type. * @param annotationType the type of annotation to look for * @return a Map with the matching beans, containing the bean names as * keys and the corresponding bean instances as values diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java b/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java index 539b2534248d..d42cf3befbc9 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,10 +36,10 @@ public class NoSuchBeanDefinitionException extends BeansException { @Nullable - private String beanName; + private final String beanName; @Nullable - private ResolvableType resolvableType; + private final ResolvableType resolvableType; /** @@ -49,6 +49,7 @@ public class NoSuchBeanDefinitionException extends BeansException { public NoSuchBeanDefinitionException(String name) { super("No bean named '" + name + "' available"); this.beanName = name; + this.resolvableType = null; } /** @@ -59,6 +60,7 @@ public NoSuchBeanDefinitionException(String name) { public NoSuchBeanDefinitionException(String name, String message) { super("No bean named '" + name + "' available: " + message); this.beanName = name; + this.resolvableType = null; } /** @@ -85,6 +87,7 @@ public NoSuchBeanDefinitionException(Class type, String message) { */ public NoSuchBeanDefinitionException(ResolvableType type) { super("No qualifying bean of type '" + type + "' available"); + this.beanName = null; this.resolvableType = type; } @@ -96,6 +99,7 @@ public NoSuchBeanDefinitionException(ResolvableType type) { */ public NoSuchBeanDefinitionException(ResolvableType type, String message) { super("No qualifying bean of type '" + type + "' available: " + message); + this.beanName = null; this.resolvableType = type; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java b/spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java index 1652dce7ebc7..b3e6e287ca7d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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 java.util.Arrays; import java.util.Collection; +import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; @@ -33,10 +34,10 @@ @SuppressWarnings("serial") public class NoUniqueBeanDefinitionException extends NoSuchBeanDefinitionException { - private int numberOfBeansFound; + private final int numberOfBeansFound; @Nullable - private Collection beanNamesFound; + private final Collection beanNamesFound; /** @@ -48,6 +49,7 @@ public class NoUniqueBeanDefinitionException extends NoSuchBeanDefinitionExcepti public NoUniqueBeanDefinitionException(Class type, int numberOfBeansFound, String message) { super(type, message); this.numberOfBeansFound = numberOfBeansFound; + this.beanNamesFound = null; } /** @@ -56,8 +58,9 @@ public NoUniqueBeanDefinitionException(Class type, int numberOfBeansFound, St * @param beanNamesFound the names of all matching beans (as a Collection) */ public NoUniqueBeanDefinitionException(Class type, Collection beanNamesFound) { - this(type, beanNamesFound.size(), "expected single matching bean but found " + beanNamesFound.size() + ": " + + super(type, "expected single matching bean but found " + beanNamesFound.size() + ": " + StringUtils.collectionToCommaDelimitedString(beanNamesFound)); + this.numberOfBeansFound = beanNamesFound.size(); this.beanNamesFound = beanNamesFound; } @@ -70,6 +73,29 @@ public NoUniqueBeanDefinitionException(Class type, String... beanNamesFound) this(type, Arrays.asList(beanNamesFound)); } + /** + * Create a new {@code NoUniqueBeanDefinitionException}. + * @param type required type of the non-unique bean + * @param beanNamesFound the names of all matching beans (as a Collection) + * @since 5.1 + */ + public NoUniqueBeanDefinitionException(ResolvableType type, Collection beanNamesFound) { + super(type, "expected single matching bean but found " + beanNamesFound.size() + ": " + + StringUtils.collectionToCommaDelimitedString(beanNamesFound)); + this.numberOfBeansFound = beanNamesFound.size(); + this.beanNamesFound = beanNamesFound; + } + + /** + * Create a new {@code NoUniqueBeanDefinitionException}. + * @param type required type of the non-unique bean + * @param beanNamesFound the names of all matching beans (as an array) + * @since 5.1 + */ + public NoUniqueBeanDefinitionException(ResolvableType type, String... beanNamesFound) { + this(type, Arrays.asList(beanNamesFound)); + } + /** * Return the number of beans found when only one matching bean was expected. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/ObjectFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/ObjectFactory.java index 9546b6ee736a..06ee95c0a3d5 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/ObjectFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/ObjectFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,6 +33,7 @@ * * @author Colin Sampaleanu * @since 1.0.2 + * @param the object type * @see FactoryBean */ @FunctionalInterface diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java b/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java index 527aa30816bc..eb480c44c884 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,8 +16,10 @@ package org.springframework.beans.factory; +import java.util.Iterator; import java.util.function.Consumer; import java.util.function.Supplier; +import java.util.stream.Stream; import org.springframework.beans.BeansException; import org.springframework.lang.Nullable; @@ -26,10 +28,17 @@ * A variant of {@link ObjectFactory} designed specifically for injection points, * allowing for programmatic optionality and lenient not-unique handling. * + *

As of 5.1, this interface extends {@link Iterable} and provides {@link Stream} + * support. It can be therefore be used in {@code for} loops, provides {@link #forEach} + * iteration and allows for collection-style {@link #stream} access. + * * @author Juergen Hoeller * @since 4.3 + * @param the object type + * @see BeanFactory#getBeanProvider + * @see org.springframework.beans.factory.annotation.Autowired */ -public interface ObjectProvider extends ObjectFactory { +public interface ObjectProvider extends ObjectFactory, Iterable { /** * Return an instance (possibly shared or independent) of the object @@ -129,4 +138,42 @@ default void ifUnique(Consumer dependencyConsumer) throws BeansException { } } + /** + * Return an {@link Iterator} over all matching object instances, + * without specific ordering guarantees (but typically in registration order). + * @since 5.1 + * @see #stream() + */ + @Override + default Iterator iterator() { + return stream().iterator(); + } + + /** + * Return a sequential {@link Stream} over all matching object instances, + * without specific ordering guarantees (but typically in registration order). + * @since 5.1 + * @see #iterator() + * @see #orderedStream() + */ + default Stream stream() { + throw new UnsupportedOperationException("Multi element access not supported"); + } + + /** + * Return a sequential {@link Stream} over all matching object instances, + * pre-ordered according to the factory's common order comparator. + *

In a standard Spring application context, this will be ordered + * according to {@link org.springframework.core.Ordered} conventions, + * and in case of annotation-based configuration also considering the + * {@link org.springframework.core.annotation.Order} annotation, + * analogous to multi-element injection points of list/array type. + * @since 5.1 + * @see #stream() + * @see org.springframework.core.OrderComparator + */ + default Stream orderedStream() { + throw new UnsupportedOperationException("Ordered element access not supported"); + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/SmartFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/SmartFactoryBean.java index 1f16eeb00b3d..42cc6e487069 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/SmartFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/SmartFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author 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 * @since 2.0.3 + * @param the bean type * @see #isPrototype() * @see #isSingleton() */ diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java b/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java index a5de119729ca..4a39985acbca 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,7 @@ public class UnsatisfiedDependencyException extends BeanCreationException { @Nullable - private InjectionPoint injectionPoint; + private final InjectionPoint injectionPoint; /** @@ -49,6 +49,7 @@ public UnsatisfiedDependencyException( super(resourceDescription, beanName, "Unsatisfied dependency expressed through bean property '" + propertyName + "'" + (StringUtils.hasLength(msg) ? ": " + msg : "")); + this.injectionPoint = null; } /** diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java index e34f27741f0a..8954bdedb104 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -96,7 +96,7 @@ public AnnotatedGenericBeanDefinition(AnnotationMetadata metadata, MethodMetadat @Override public final AnnotationMetadata getMetadata() { - return this.metadata; + return this.metadata; } @Override 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 78a690b475f0..5171f007e5ff 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author 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,24 +46,23 @@ public BeanWiringInfo resolveWiringInfo(Object beanInstance) { } /** - * Build the BeanWiringInfo for the given Configurable annotation. + * Build the {@link BeanWiringInfo} for the given {@link Configurable} annotation. * @param beanInstance the bean instance * @param annotation the Configurable annotation found on the bean class * @return the resolved BeanWiringInfo */ protected BeanWiringInfo buildWiringInfo(Object beanInstance, Configurable annotation) { if (!Autowire.NO.equals(annotation.autowire())) { + // Autowiring by name or by type return new BeanWiringInfo(annotation.autowire().value(), annotation.dependencyCheck()); } + else if (!"".equals(annotation.value())) { + // Explicitly specified bean name for bean definition to take property values from + return new BeanWiringInfo(annotation.value(), false); + } else { - if (!"".equals(annotation.value())) { - // explicitly specified bean name - return new BeanWiringInfo(annotation.value(), false); - } - else { - // default bean name - return new BeanWiringInfo(getDefaultBeanName(beanInstance), true); - } + // Default bean name for bean definition to take property values from + return new BeanWiringInfo(getDefaultBeanName(beanInstance), true); } } 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 cfc5d9d4c412..05a9b2eae644 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,15 +23,22 @@ import java.lang.annotation.Target; /** - * Marks a constructor, field, setter method or config method as to be autowired - * by Spring's dependency injection facilities. + * Marks a constructor, field, setter method or config method as to be autowired by + * Spring's dependency injection facilities. This is an alternative to the JSR-330 + * {@link javax.inject.Inject} annotation, adding required-vs-optional semantics. * - *

Only one constructor (at max) of any given bean class may carry this annotation, - * indicating the constructor to autowire when used as a Spring bean. Such a - * constructor does not have to be public. + *

Only one constructor (at max) of any given bean class may declare this annotation + * with the 'required' parameter set to {@code true}, indicating the constructor + * to autowire when used as a Spring bean. If multiple non-required constructors + * declare the 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. * - *

Fields are injected right after construction of a bean, before any config - * methods are invoked. Such a config field does not have to be public. + *

Fields are injected right after construction of a bean, before any config methods + * are invoked. Such a config field does not have to be public. * *

Config methods may have an arbitrary name and any number of arguments; each of * those arguments will be autowired with a matching bean in the Spring container. 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 c2a44deb427d..5f71ada4b3b4 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-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. @@ -28,7 +28,6 @@ import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -60,6 +59,7 @@ import org.springframework.core.PriorityOrdered; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -75,15 +75,15 @@ *

Also supports JSR-330's {@link javax.inject.Inject @Inject} annotation, * if available, as a direct alternative to Spring's own {@code @Autowired}. * - *

Only one constructor (at max) of any given bean class may carry this - * annotation with the 'required' parameter set to {@code true}, - * indicating the constructor to autowire when used as a Spring bean. - * If multiple non-required constructors carry the 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 default constructor (if present) will be used. - * An annotated constructor does not have to be public. + *

Only one constructor (at max) of any given bean class may declare this annotation + * with the 'required' parameter set to {@code true}, indicating the constructor + * to autowire when used as a Spring bean. If multiple non-required constructors + * declare the 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. * *

Fields are injected right after construction of a bean, before any * config methods are invoked. Such a config field does not have to be public. @@ -121,7 +121,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean protected final Log logger = LogFactory.getLog(getClass()); - private final Set> autowiredAnnotationTypes = new LinkedHashSet<>(); + private final Set> autowiredAnnotationTypes = new LinkedHashSet<>(4); private String requiredParameterName = "required"; @@ -151,7 +151,7 @@ public AutowiredAnnotationBeanPostProcessor() { try { this.autowiredAnnotationTypes.add((Class) ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader())); - logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring"); + logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. @@ -162,11 +162,11 @@ 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}. + *

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

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. + * (non-Spring-specific) annotation type to indicate that a member is supposed + * to be autowired. */ public void setAutowiredAnnotationType(Class autowiredAnnotationType) { Assert.notNull(autowiredAnnotationType, "'autowiredAnnotationType' must not be null"); @@ -177,11 +177,11 @@ 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}. + *

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

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. + * (non-Spring-specific) annotation types to indicate that a member is supposed + * to be autowired. */ public void setAutowiredAnnotationTypes(Set> autowiredAnnotationTypes) { Assert.notEmpty(autowiredAnnotationTypes, "'autowiredAnnotationTypes' must not be empty"); @@ -190,8 +190,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 a parameter of the annotation that specifies whether it is required. * @see #setRequiredParameterValue(boolean) */ public void setRequiredParameterName(String requiredParameterName) { @@ -200,9 +199,8 @@ public void setRequiredParameterName(String requiredParameterName) { /** * 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}. + *

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) */ public void setRequiredParameterValue(boolean requiredParameterValue) { @@ -234,6 +232,12 @@ public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, C metadata.checkConfigMembers(beanDefinition); } + @Override + public void resetBeanDefinition(String beanName) { + this.lookupMethodsChecked.remove(beanName); + this.injectionMetadataCache.remove(beanName); + } + @Override @Nullable public Constructor[] determineCandidateConstructors(Class beanClass, final String beanName) @@ -241,25 +245,33 @@ public Constructor[] determineCandidateConstructors(Class beanClass, final // Let's check for lookup methods here.. if (!this.lookupMethodsChecked.contains(beanName)) { - try { - ReflectionUtils.doWithMethods(beanClass, method -> { - Lookup lookup = method.getAnnotation(Lookup.class); - if (lookup != null) { - Assert.state(beanFactory != null, "No BeanFactory available"); - LookupOverride override = new LookupOverride(method, lookup.value()); - try { - RootBeanDefinition mbd = (RootBeanDefinition) beanFactory.getMergedBeanDefinition(beanName); - mbd.getMethodOverrides().addOverride(override); - } - catch (NoSuchBeanDefinitionException ex) { - throw new BeanCreationException(beanName, - "Cannot apply @Lookup to beans without corresponding bean definition"); - } + if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) { + try { + Class targetClass = beanClass; + do { + ReflectionUtils.doWithLocalMethods(targetClass, method -> { + Lookup lookup = method.getAnnotation(Lookup.class); + if (lookup != null) { + Assert.state(this.beanFactory != null, "No BeanFactory available"); + LookupOverride override = new LookupOverride(method, lookup.value()); + try { + RootBeanDefinition mbd = (RootBeanDefinition) this.beanFactory.getMergedBeanDefinition(beanName); + mbd.getMethodOverrides().addOverride(override); + } + catch (NoSuchBeanDefinitionException ex) { + throw new BeanCreationException(beanName, + "Cannot apply @Lookup to beans without corresponding bean definition"); + } + } + }); + targetClass = targetClass.getSuperclass(); } - }); - } - catch (IllegalStateException ex) { - throw new BeanCreationException(beanName, "Lookup method resolution failed", ex); + while (targetClass != null && targetClass != Object.class); + + } + catch (IllegalStateException ex) { + throw new BeanCreationException(beanName, "Lookup method resolution failed", ex); + } } this.lookupMethodsChecked.add(beanName); } @@ -335,8 +347,8 @@ else if (candidate.getParameterCount() == 0) { if (defaultConstructor != null) { candidates.add(defaultConstructor); } - else if (candidates.size() == 1 && logger.isWarnEnabled()) { - logger.warn("Inconsistent constructor declaration on bean with name '" + beanName + + else if (candidates.size() == 1 && logger.isInfoEnabled()) { + logger.info("Inconsistent constructor declaration on bean with name '" + beanName + "': single autowire-marked constructor flagged as optional - " + "this constructor is effectively required since there is no " + "default constructor to fall back to: " + candidates.get(0)); @@ -347,8 +359,8 @@ else if (candidates.size() == 1 && logger.isWarnEnabled()) { else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) { candidateConstructors = new Constructor[] {rawCandidates[0]}; } - else if (nonSyntheticConstructors == 2 && primaryConstructor != null - && defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) { + else if (nonSyntheticConstructors == 2 && primaryConstructor != null && + defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) { candidateConstructors = new Constructor[] {primaryConstructor, defaultConstructor}; } else if (nonSyntheticConstructors == 1 && primaryConstructor != null) { @@ -365,9 +377,7 @@ else if (nonSyntheticConstructors == 1 && primaryConstructor != null) { } @Override - public PropertyValues postProcessPropertyValues( - PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException { - + public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); @@ -381,6 +391,14 @@ public PropertyValues postProcessPropertyValues( return pvs; } + @Deprecated + @Override + public PropertyValues postProcessPropertyValues( + PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) { + + return postProcessProperties(pvs, bean, beanName); + } + /** * 'Native' processing method for direct calls with an arbitrary target instance, * resolving all of its fields and methods which are annotated with {@code @Autowired}. @@ -424,18 +442,22 @@ private InjectionMetadata findAutowiringMetadata(String beanName, Class clazz } private InjectionMetadata buildAutowiringMetadata(final Class clazz) { - LinkedList elements = new LinkedList<>(); + if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) { + return InjectionMetadata.EMPTY; + } + + List elements = new ArrayList<>(); Class targetClass = clazz; do { - final LinkedList currElements = new LinkedList<>(); + final List currElements = new ArrayList<>(); ReflectionUtils.doWithLocalFields(targetClass, field -> { AnnotationAttributes ann = findAutowiredAnnotation(field); if (ann != null) { if (Modifier.isStatic(field.getModifiers())) { - if (logger.isWarnEnabled()) { - logger.warn("Autowired annotation is not supported on static fields: " + field); + if (logger.isInfoEnabled()) { + logger.info("Autowired annotation is not supported on static fields: " + field); } return; } @@ -452,14 +474,14 @@ private InjectionMetadata buildAutowiringMetadata(final Class clazz) { AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod); if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { if (Modifier.isStatic(method.getModifiers())) { - if (logger.isWarnEnabled()) { - logger.warn("Autowired annotation is not supported on static methods: " + method); + if (logger.isInfoEnabled()) { + logger.info("Autowired annotation is not supported on static methods: " + method); } return; } if (method.getParameterCount() == 0) { - if (logger.isWarnEnabled()) { - logger.warn("Autowired annotation should only be used on methods with parameters: " + + if (logger.isInfoEnabled()) { + logger.info("Autowired annotation should only be used on methods with parameters: " + method); } } @@ -474,12 +496,12 @@ private InjectionMetadata buildAutowiringMetadata(final Class clazz) { } while (targetClass != null && targetClass != Object.class); - return new InjectionMetadata(clazz, elements); + return InjectionMetadata.forElements(elements, clazz); } @Nullable private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) { - if (ao.getAnnotations().length > 0) { + if (ao.getAnnotations().length > 0) { // autowiring annotations have to be local for (Class type : this.autowiredAnnotationTypes) { AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type); if (attributes != null) { @@ -526,8 +548,8 @@ private void registerDependentBeans(@Nullable String beanName, Set autow if (this.beanFactory != null && this.beanFactory.containsBean(autowiredBeanName)) { this.beanFactory.registerDependentBean(autowiredBeanName, beanName); } - if (logger.isDebugEnabled()) { - logger.debug("Autowiring by type from bean name '" + beanName + + if (logger.isTraceEnabled()) { + logger.trace("Autowiring by type from bean name '" + beanName + "' to bean named '" + autowiredBeanName + "'"); } } @@ -541,7 +563,7 @@ private void registerDependentBeans(@Nullable String beanName, Set autow private Object resolvedCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) { if (cachedArgument instanceof DependencyDescriptor) { DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument; - Assert.state(beanFactory != null, "No BeanFactory available"); + Assert.state(this.beanFactory != null, "No BeanFactory available"); return this.beanFactory.resolveDependency(descriptor, beanName, null, null); } else { @@ -698,7 +720,7 @@ protected void inject(Object bean, @Nullable String beanName, @Nullable Property ReflectionUtils.makeAccessible(method); method.invoke(bean, arguments); } - catch (InvocationTargetException ex){ + catch (InvocationTargetException ex) { throw ex.getTargetException(); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java index 8a9b4b6414e4..d5ec4f2b977b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author 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,16 +17,18 @@ package org.springframework.beans.factory.annotation; import java.lang.reflect.Method; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.function.Predicate; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.AutowireCandidateQualifier; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -35,8 +37,8 @@ import org.springframework.util.Assert; /** - * Convenience methods performing bean lookups related to annotations, for example - * Spring's {@link Qualifier @Qualifier} annotation. + * Convenience methods performing bean lookups related to Spring-specific annotations, + * for example Spring's {@link Qualifier @Qualifier} annotation. * * @author Juergen Hoeller * @author Chris Beams @@ -45,27 +47,52 @@ */ public abstract class BeanFactoryAnnotationUtils { + /** + * Retrieve all bean of type {@code T} from the given {@code BeanFactory} declaring a + * qualifier (e.g. via {@code } or {@code @Qualifier}) matching the given + * qualifier, or having a bean name matching the given qualifier. + * @param beanFactory the factory to get the target beans from (also searching ancestors) + * @param beanType the type of beans to retrieve + * @param qualifier the qualifier for selecting among all type matches + * @return the matching beans of type {@code T} + * @throws BeansException if any of the matching beans could not be created + * @since 5.1.1 + * @see BeanFactoryUtils#beansOfTypeIncludingAncestors(ListableBeanFactory, Class) + */ + public static Map qualifiedBeansOfType( + ListableBeanFactory beanFactory, Class beanType, String qualifier) throws BeansException { + + String[] candidateBeans = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, beanType); + Map result = new LinkedHashMap<>(4); + for (String beanName : candidateBeans) { + if (isQualifierMatch(qualifier::equals, beanName, beanFactory)) { + result.put(beanName, beanFactory.getBean(beanName, beanType)); + } + } + return result; + } + /** * Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a * qualifier (e.g. via {@code } or {@code @Qualifier}) matching the given * qualifier, or having a bean name matching the given qualifier. - * @param beanFactory the BeanFactory to get the target bean from + * @param beanFactory the factory to get the target bean from (also searching ancestors) * @param beanType the type of bean to retrieve * @param qualifier the qualifier for selecting between multiple bean matches * @return the matching bean of type {@code T} (never {@code null}) * @throws NoUniqueBeanDefinitionException if multiple matching beans of type {@code T} found * @throws NoSuchBeanDefinitionException if no matching bean of type {@code T} found * @throws BeansException if the bean could not be created - * @see BeanFactory#getBean(Class) + * @see BeanFactoryUtils#beanOfTypeIncludingAncestors(ListableBeanFactory, Class) */ public static T qualifiedBeanOfType(BeanFactory beanFactory, Class beanType, String qualifier) throws BeansException { Assert.notNull(beanFactory, "BeanFactory must not be null"); - if (beanFactory instanceof ConfigurableListableBeanFactory) { + if (beanFactory instanceof ListableBeanFactory) { // Full qualifier matching supported. - return qualifiedBeanOfType((ConfigurableListableBeanFactory) beanFactory, beanType, qualifier); + return qualifiedBeanOfType((ListableBeanFactory) beanFactory, beanType, qualifier); } else if (beanFactory.containsBean(qualifier)) { // Fallback: target bean at least found by bean name. @@ -82,12 +109,12 @@ else if (beanFactory.containsBean(qualifier)) { /** * Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a qualifier * (e.g. {@code } or {@code @Qualifier}) matching the given qualifier). - * @param bf the BeanFactory to get the target bean from + * @param bf the factory to get the target bean from * @param beanType the type of bean to retrieve * @param qualifier the qualifier for selecting between multiple bean matches * @return the matching bean of type {@code T} (never {@code null}) */ - private static T qualifiedBeanOfType(ConfigurableListableBeanFactory bf, Class beanType, String qualifier) { + private static T qualifiedBeanOfType(ListableBeanFactory bf, Class beanType, String qualifier) { String[] candidateBeans = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(bf, beanType); String matchingBean = null; for (String beanName : candidateBeans) { @@ -115,14 +142,14 @@ else if (bf.containsBean(qualifier)) { * Check whether the named bean declares a qualifier of the given name. * @param qualifier the qualifier to match * @param beanName the name of the candidate bean - * @param beanFactory the {@code BeanFactory} from which to retrieve the named bean + * @param beanFactory the factory from which to retrieve the named bean * @return {@code true} if either the bean definition (in the XML case) * or the bean's factory method (in the {@code @Bean} case) defines a matching * qualifier value (through {@code } or {@code @Qualifier}) * @since 5.0 */ - public static boolean isQualifierMatch(Predicate qualifier, String beanName, - @Nullable BeanFactory beanFactory) { + public static boolean isQualifierMatch( + Predicate qualifier, String beanName, @Nullable BeanFactory beanFactory) { // Try quick bean name or alias match first... if (qualifier.test(beanName)) { @@ -135,6 +162,7 @@ public static boolean isQualifierMatch(Predicate qualifier, String beanN } } try { + Class beanType = beanFactory.getType(beanName); if (beanFactory instanceof ConfigurableBeanFactory) { BeanDefinition bd = ((ConfigurableBeanFactory) beanFactory).getMergedBeanDefinition(beanName); // Explicit qualifier metadata on bean definition? (typically in XML definition) @@ -160,7 +188,6 @@ public static boolean isQualifierMatch(Predicate qualifier, String beanN } } // Corresponding qualifier on bean implementation class? (for custom user types) - Class beanType = beanFactory.getType(beanName); if (beanType != null) { Qualifier targetAnnotation = AnnotationUtils.getAnnotation(beanType, Qualifier.class); if (targetAnnotation != null) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java index 422e61f57a5c..914204e8f1a9 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.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. @@ -23,9 +23,12 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashSet; -import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -40,6 +43,7 @@ import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; @@ -76,6 +80,24 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, Serializable { + private final transient LifecycleMetadata emptyLifecycleMetadata = + new LifecycleMetadata(Object.class, Collections.emptyList(), Collections.emptyList()) { + @Override + public void checkConfigMembers(RootBeanDefinition beanDefinition) { + } + @Override + public void invokeInitMethods(Object target, String beanName) { + } + @Override + public void invokeDestroyMethods(Object target, String beanName) { + } + @Override + public boolean hasDestroyMethods() { + return false; + } + }; + + protected transient Log logger = LogFactory.getLog(getClass()); @Nullable @@ -155,7 +177,7 @@ public void postProcessBeforeDestruction(Object bean, String beanName) throws Be metadata.invokeDestroyMethods(bean, beanName); } catch (InvocationTargetException ex) { - String msg = "Invocation of destroy method failed on bean with name '" + beanName + "'"; + String msg = "Destroy method on bean with name '" + beanName + "' threw an exception"; if (logger.isDebugEnabled()) { logger.warn(msg, ex.getTargetException()); } @@ -164,7 +186,7 @@ public void postProcessBeforeDestruction(Object bean, String beanName) throws Be } } catch (Throwable ex) { - logger.error("Failed to invoke destroy method on bean with name '" + beanName + "'", ex); + logger.warn("Failed to invoke destroy method on bean with name '" + beanName + "'", ex); } } @@ -195,27 +217,30 @@ private LifecycleMetadata findLifecycleMetadata(Class clazz) { } private LifecycleMetadata buildLifecycleMetadata(final Class clazz) { - final boolean debug = logger.isDebugEnabled(); - LinkedList initMethods = new LinkedList<>(); - LinkedList destroyMethods = new LinkedList<>(); + if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) { + return this.emptyLifecycleMetadata; + } + + List initMethods = new ArrayList<>(); + List destroyMethods = new ArrayList<>(); Class targetClass = clazz; do { - final LinkedList currInitMethods = new LinkedList<>(); - final LinkedList currDestroyMethods = new LinkedList<>(); + final List currInitMethods = new ArrayList<>(); + final List currDestroyMethods = new ArrayList<>(); ReflectionUtils.doWithLocalMethods(targetClass, method -> { - if (initAnnotationType != null && method.isAnnotationPresent(initAnnotationType)) { + if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) { LifecycleElement element = new LifecycleElement(method); currInitMethods.add(element); - if (debug) { - logger.debug("Found init method on class [" + clazz.getName() + "]: " + method); + if (logger.isTraceEnabled()) { + logger.trace("Found init method on class [" + clazz.getName() + "]: " + method); } } - if (destroyAnnotationType != null && method.isAnnotationPresent(destroyAnnotationType)) { + if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) { currDestroyMethods.add(new LifecycleElement(method)); - if (debug) { - logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method); + if (logger.isTraceEnabled()) { + logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method); } } }); @@ -226,7 +251,8 @@ private LifecycleMetadata buildLifecycleMetadata(final Class clazz) { } while (targetClass != null && targetClass != Object.class); - return new LifecycleMetadata(clazz, initMethods, destroyMethods); + return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata : + new LifecycleMetadata(clazz, initMethods, destroyMethods)); } @@ -275,8 +301,8 @@ public void checkConfigMembers(RootBeanDefinition beanDefinition) { if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) { beanDefinition.registerExternallyManagedInitMethod(methodIdentifier); checkedInitMethods.add(element); - if (logger.isDebugEnabled()) { - logger.debug("Registered init method on class [" + this.targetClass.getName() + "]: " + element); + if (logger.isTraceEnabled()) { + logger.trace("Registered init method on class [" + this.targetClass.getName() + "]: " + element); } } } @@ -286,8 +312,8 @@ public void checkConfigMembers(RootBeanDefinition beanDefinition) { if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) { beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier); checkedDestroyMethods.add(element); - if (logger.isDebugEnabled()) { - logger.debug("Registered destroy method on class [" + this.targetClass.getName() + "]: " + element); + if (logger.isTraceEnabled()) { + logger.trace("Registered destroy method on class [" + this.targetClass.getName() + "]: " + element); } } } @@ -300,10 +326,9 @@ public void invokeInitMethods(Object target, String beanName) throws Throwable { Collection initMethodsToIterate = (checkedInitMethods != null ? checkedInitMethods : this.initMethods); if (!initMethodsToIterate.isEmpty()) { - boolean debug = logger.isDebugEnabled(); for (LifecycleElement element : initMethodsToIterate) { - if (debug) { - logger.debug("Invoking init method on bean '" + beanName + "': " + element.getMethod()); + if (logger.isTraceEnabled()) { + logger.trace("Invoking init method on bean '" + beanName + "': " + element.getMethod()); } element.invoke(target); } @@ -315,10 +340,9 @@ public void invokeDestroyMethods(Object target, String beanName) throws Throwabl Collection destroyMethodsToUse = (checkedDestroyMethods != null ? checkedDestroyMethods : this.destroyMethods); if (!destroyMethodsToUse.isEmpty()) { - boolean debug = logger.isDebugEnabled(); for (LifecycleElement element : destroyMethodsToUse) { - if (debug) { - logger.debug("Invoking destroy method on bean '" + beanName + "': " + element.getMethod()); + if (logger.isTraceEnabled()) { + logger.trace("Invoking destroy method on bean '" + beanName + "': " + element.getMethod()); } element.invoke(target); } 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 58c7296f6f83..cc0ddc52e1c9 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-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. @@ -22,6 +22,7 @@ import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; @@ -47,6 +48,23 @@ */ public class InjectionMetadata { + /** + * An empty {@code InjectionMetadata} instance with no-op callbacks. + * @since 5.2 + */ + public static final InjectionMetadata EMPTY = new InjectionMetadata(Object.class, Collections.emptyList()) { + @Override + public void checkConfigMembers(RootBeanDefinition beanDefinition) { + } + @Override + public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) { + } + @Override + public void clear(@Nullable PropertyValues pvs) { + } + }; + + private static final Log logger = LogFactory.getLog(InjectionMetadata.class); private final Class targetClass; @@ -57,6 +75,14 @@ public class InjectionMetadata { private volatile Set checkedElements; + /** + * Create a new {@code InjectionMetadata instance}. + *

Preferably use {@link #forElements} for reusing the {@link #EMPTY} + * instance in case of no elements. + * @param targetClass the target class + * @param elements the associated elements to inject + * @see #forElements + */ public InjectionMetadata(Class targetClass, Collection elements) { this.targetClass = targetClass; this.injectedElements = elements; @@ -70,8 +96,8 @@ public void checkConfigMembers(RootBeanDefinition beanDefinition) { if (!beanDefinition.isExternallyManagedConfigMember(member)) { beanDefinition.registerExternallyManagedConfigMember(member); checkedElements.add(element); - if (logger.isDebugEnabled()) { - logger.debug("Registered injected element on class [" + this.targetClass.getName() + "]: " + element); + if (logger.isTraceEnabled()) { + logger.trace("Registered injected element on class [" + this.targetClass.getName() + "]: " + element); } } } @@ -83,10 +109,9 @@ public void inject(Object target, @Nullable String beanName, @Nullable PropertyV Collection elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) { - boolean debug = logger.isDebugEnabled(); for (InjectedElement element : elementsToIterate) { - if (debug) { - logger.debug("Processing injected element of bean '" + beanName + "': " + element); + if (logger.isTraceEnabled()) { + logger.trace("Processing injected element of bean '" + beanName + "': " + element); } element.inject(target, beanName, pvs); } @@ -94,6 +119,7 @@ public void inject(Object target, @Nullable String beanName, @Nullable PropertyV } /** + * Clear property skipping for the contained elements. * @since 3.2.13 */ public void clear(@Nullable PropertyValues pvs) { @@ -108,11 +134,32 @@ 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 + * @since 5.2 + */ + public static InjectionMetadata forElements(Collection elements, Class clazz) { + return (elements.isEmpty() ? InjectionMetadata.EMPTY : new InjectionMetadata(clazz, elements)); + } + + /** + * Check whether the given injection metadata needs to be refreshed. + * @param metadata the existing metadata instance + * @param clazz the current target class + * @return {@code true} indicating a refresh, {@code false} otherwise + */ public static boolean needsRefresh(@Nullable InjectionMetadata metadata, Class clazz) { return (metadata == null || metadata.targetClass != clazz); } + /** + * A single injected element. + */ public abstract static class InjectedElement { protected final Member member; @@ -226,6 +273,7 @@ else if (pvs instanceof MutablePropertyValues) { } /** + * Clear property skipping for this element. * @since 3.2.13 */ protected void clearPropertySkipping(@Nullable PropertyValues pvs) { 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 19a642423140..0296dc128cf9 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-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -325,6 +325,21 @@ public boolean isRequired(DependencyDescriptor descriptor) { return (autowired == null || autowired.required()); } + /** + * Determine whether the given dependency declares a qualifier annotation. + * @see #isQualifier(Class) + * @see Qualifier + */ + @Override + public boolean hasQualifier(DependencyDescriptor descriptor) { + for (Annotation ann : descriptor.getAnnotations()) { + if (isQualifier(ann.annotationType())) { + return true; + } + } + return false; + } + /** * Determine whether the given dependency declares a value annotation. * @see Value @@ -347,10 +362,12 @@ public Object getSuggestedValue(DependencyDescriptor descriptor) { */ @Nullable protected Object findValue(Annotation[] annotationsToSearch) { - AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes( - AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType); - if (attr != null) { - return extractValue(attr); + if (annotationsToSearch.length > 0) { // qualifier annotations have to be local + AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes( + AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType); + if (attr != null) { + return extractValue(attr); + } } return null; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Required.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Required.java index bb342c7854c9..915543a58847 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Required.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Required.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author 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,10 @@ * @author Rob Harrop * @since 2.0 * @see RequiredAnnotationBeanPostProcessor + * @deprecated as of 5.1, in favor of using constructor injection for required settings + * (or a custom {@link org.springframework.beans.factory.InitializingBean} implementation) */ +@Deprecated @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Required { 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 273f77410c15..b1a9be2d3336 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 @@ -25,7 +25,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; @@ -72,7 +71,10 @@ * @since 2.0 * @see #setRequiredAnnotationType * @see Required + * @deprecated as of 5.1, in favor of using constructor injection for required settings + * (or a custom {@link org.springframework.beans.factory.InitializingBean} implementation) */ +@Deprecated public class RequiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware { @@ -93,7 +95,7 @@ public class RequiredAnnotationBeanPostProcessor extends InstantiationAwareBeanP private ConfigurableListableBeanFactory beanFactory; /** - * Cache for validated bean names, skipping re-validation for the same bean + * Cache for validated bean names, skipping re-validation for the same bean. */ private final Set validatedBeanNames = Collections.newSetFromMap(new ConcurrentHashMap<>(64)); @@ -142,7 +144,7 @@ public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, C @Override public PropertyValues postProcessPropertyValues( - PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { + PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) { if (!this.validatedBeanNames.contains(beanName)) { if (!shouldSkip(this.beanFactory, beanName)) { 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 88ae5cc0c7ea..33d9ba7b4f8c 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-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. @@ -30,7 +30,7 @@ * for dynamic resolution of handler method parameters, e.g. in Spring MVC. * *

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

Note that actual processing of the {@code @Value} annotation is performed * by a {@link org.springframework.beans.factory.config.BeanPostProcessor @@ -55,7 +55,7 @@ public @interface Value { /** - * The actual value expression: e.g. "#{systemProperties.myProp}". + * The actual value expression: for example {@code #{systemProperties.myProp}}. */ String value(); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java index fe1dcf8bdd43..d961eea9a727 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,13 +56,14 @@ * @author Juergen Hoeller * @author Keith Donald * @since 1.0.2 + * @param the bean type * @see #setSingleton * @see #createInstance() */ public abstract class AbstractFactoryBean implements FactoryBean, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean { - /** Logger available to subclasses */ + /** Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); private boolean singleton = true; @@ -160,7 +161,7 @@ public final T getObject() throws Exception { } /** - * Determine an 'eager singleton' instance, exposed in case of a + * Determine an 'early singleton' instance, exposed in case of a * circular reference. Not called in a non-circular scenario. */ @SuppressWarnings("unchecked") @@ -225,7 +226,7 @@ public void destroy() throws Exception { * FactoryBean is supposed to implement, for use with an 'early singleton * proxy' that will be exposed in case of a circular reference. *

The default implementation returns this FactoryBean's object type, - * provided that it is an interface, or {@code null} else. The latter + * provided that it is an interface, or {@code null} otherwise. The latter * indicates that early singleton access is not supported by this FactoryBean. * This will lead to a FactoryBeanNotInitializedException getting thrown. * @return the interfaces to use for 'early singletons', diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java index 9facb43e1886..4f0818c396b0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.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. @@ -107,6 +107,18 @@ public interface AutowireCapableBeanFactory extends BeanFactory { @Deprecated int AUTOWIRE_AUTODETECT = 4; + /** + * Suffix for the "original instance" convention when initializing an existing + * bean instance: to be appended to the fully-qualified bean class name, + * e.g. "com.mypackage.MyClass.ORIGINAL", in order to enforce the given instance + * to be returned, i.e. no proxies etc. + * @since 5.1 + * @see #initializeBean(Object, String) + * @see #applyBeanPostProcessorsBeforeInitialization(Object, String) + * @see #applyBeanPostProcessorsAfterInitialization(Object, String) + */ + String ORIGINAL_INSTANCE_SUFFIX = ".ORIGINAL"; + //------------------------------------------------------------------------- // Typical methods for creating and populating external bean instances @@ -118,7 +130,7 @@ public interface AutowireCapableBeanFactory extends BeanFactory { * {@link BeanPostProcessor BeanPostProcessors}. *

Note: This is intended for creating a fresh instance, populating annotated * fields and methods as well as applying all standard bean initialization callbacks. - * It does not imply traditional by-name or by-type autowiring of properties; + * It does not imply traditional by-name or by-type autowiring of properties; * use {@link #createBean(Class, int, boolean)} for those purposes. * @param beanClass the class of the bean to create * @return the new bean instance @@ -264,9 +276,12 @@ void autowireBeanProperties(Object existingBean, int autowireMode, boolean depen * for callbacks but not checked against the registered bean definitions. * @param existingBean the existing bean instance * @param beanName the name of the bean, to be passed to it if necessary - * (only passed to {@link BeanPostProcessor BeanPostProcessors}) + * (only passed to {@link BeanPostProcessor BeanPostProcessors}; + * can follow the {@link #ORIGINAL_INSTANCE_SUFFIX} convention in order to + * enforce the given instance to be returned, i.e. no proxies etc) * @return the bean instance to use, either the original or a wrapped one * @throws BeansException if the initialization failed + * @see #ORIGINAL_INSTANCE_SUFFIX */ Object initializeBean(Object existingBean, String beanName) throws BeansException; @@ -274,11 +289,15 @@ void autowireBeanProperties(Object existingBean, int autowireMode, boolean depen * Apply {@link BeanPostProcessor BeanPostProcessors} to the given existing bean * instance, invoking their {@code postProcessBeforeInitialization} methods. * The returned bean instance may be a wrapper around the original. - * @param existingBean the new bean instance - * @param beanName the name of the bean + * @param existingBean the existing bean instance + * @param beanName the name of the bean, to be passed to it if necessary + * (only passed to {@link BeanPostProcessor BeanPostProcessors}; + * can follow the {@link #ORIGINAL_INSTANCE_SUFFIX} convention in order to + * enforce the given instance to be returned, i.e. no proxies etc) * @return the bean instance to use, either the original or a wrapped one * @throws BeansException if any post-processing failed * @see BeanPostProcessor#postProcessBeforeInitialization + * @see #ORIGINAL_INSTANCE_SUFFIX */ Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException; @@ -287,11 +306,15 @@ Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String b * Apply {@link BeanPostProcessor BeanPostProcessors} to the given existing bean * instance, invoking their {@code postProcessAfterInitialization} methods. * The returned bean instance may be a wrapper around the original. - * @param existingBean the new bean instance - * @param beanName the name of the bean + * @param existingBean the existing bean instance + * @param beanName the name of the bean, to be passed to it if necessary + * (only passed to {@link BeanPostProcessor BeanPostProcessors}; + * can follow the {@link #ORIGINAL_INSTANCE_SUFFIX} convention in order to + * enforce the given instance to be returned, i.e. no proxies etc) * @return the bean instance to use, either the original or a wrapped one * @throws BeansException if any post-processing failed * @see BeanPostProcessor#postProcessAfterInitialization + * @see #ORIGINAL_INSTANCE_SUFFIX */ Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException; @@ -316,8 +339,7 @@ Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String be * including its bean name. *

This is effectively a variant of {@link #getBean(Class)} which preserves the * bean name of the matching instance. - * @param requiredType type the bean must match; can be an interface or superclass. - * {@code null} is disallowed. + * @param requiredType type the bean must match; can be an interface or superclass * @return the bean name plus bean instance * @throws NoSuchBeanDefinitionException if no matching bean was found * @throws NoUniqueBeanDefinitionException if more than one matching bean was found @@ -327,6 +349,22 @@ Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String be */ NamedBeanHolder resolveNamedBean(Class requiredType) throws BeansException; + /** + * Resolve a bean instance for the given bean name, providing a dependency descriptor + * for exposure to target factory methods. + *

This is effectively a variant of {@link #getBean(String, Class)} which supports + * factory methods with an {@link org.springframework.beans.factory.InjectionPoint} + * argument. + * @param name the name of the bean to look up + * @param descriptor the dependency descriptor for the requesting injection point + * @return the corresponding bean instance + * @throws NoSuchBeanDefinitionException if there is no bean with the specified name + * @throws BeansException if the bean could not be created + * @since 5.1.5 + * @see #getBean(String, Class) + */ + Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException; + /** * Resolve the specified dependency against the beans defined in this factory. * @param descriptor the descriptor for the dependency (field/method/constructor) 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 e798b8a399fa..c1f4e97634a8 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-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. @@ -27,8 +27,8 @@ * concrete implementations. * *

This is just a minimal interface: The main intention is to allow a - * {@link BeanFactoryPostProcessor} such as {@link PropertyPlaceholderConfigurer} - * to introspect and modify property values and other bean metadata. + * {@link BeanFactoryPostProcessor} to introspect and modify property values + * and other bean metadata. * * @author Juergen Hoeller * @author Rob Harrop @@ -242,28 +242,42 @@ default boolean hasPropertyValues() { return !getPropertyValues().isEmpty(); } + /** + * Set the name of the initializer method. + * @since 5.1 + */ + void setInitMethodName(@Nullable String initMethodName); - // Read-only attributes + /** + * Return the name of the initializer method. + * @since 5.1 + */ + @Nullable + String getInitMethodName(); /** - * Return whether this a Singleton, with a single, shared instance - * returned on all calls. - * @see #SCOPE_SINGLETON + * Set the name of the destroy method. + * @since 5.1 */ - boolean isSingleton(); + void setDestroyMethodName(@Nullable String destroyMethodName); /** - * Return whether this a Prototype, with an independent instance - * returned for each call. - * @since 3.0 - * @see #SCOPE_PROTOTYPE + * Return the name of the destroy method. + * @since 5.1 */ - boolean isPrototype(); + @Nullable + String getDestroyMethodName(); /** - * Return whether this bean is "abstract", that is, not meant to be instantiated. + * Set the role hint for this {@code BeanDefinition}. The role hint + * provides the frameworks as well as tools with an indication of + * the role and importance of a particular {@code BeanDefinition}. + * @since 5.1 + * @see #ROLE_APPLICATION + * @see #ROLE_SUPPORT + * @see #ROLE_INFRASTRUCTURE */ - boolean isAbstract(); + void setRole(int role); /** * Get the role hint for this {@code BeanDefinition}. The role hint @@ -275,12 +289,41 @@ default boolean hasPropertyValues() { */ int getRole(); + /** + * Set a human-readable description of this bean definition. + * @since 5.1 + */ + void setDescription(@Nullable String description); + /** * Return a human-readable description of this bean definition. */ @Nullable String getDescription(); + + // Read-only attributes + + /** + * Return whether this a Singleton, with a single, shared instance + * returned on all calls. + * @see #SCOPE_SINGLETON + */ + boolean isSingleton(); + + /** + * Return whether this a Prototype, with an independent instance + * returned for each call. + * @since 3.0 + * @see #SCOPE_PROTOTYPE + */ + boolean isPrototype(); + + /** + * Return whether this bean is "abstract", that is, not meant to be instantiated. + */ + boolean isAbstract(); + /** * Return a description of the resource that this bean definition * came from (for the purpose of showing context in case of errors). diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinitionHolder.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinitionHolder.java index 8556c3affbf4..376fa8602d89 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinitionHolder.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinitionHolder.java @@ -150,9 +150,7 @@ public String getShortDescription() { * @see #getBeanDefinition() */ public String getLongDescription() { - StringBuilder sb = new StringBuilder(getShortDescription()); - sb.append(": ").append(this.beanDefinition); - return sb.toString(); + return getShortDescription() + ": " + this.beanDefinition; } /** diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinitionVisitor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinitionVisitor.java index 77e94d7eabc8..67620b5783c9 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinitionVisitor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinitionVisitor.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. @@ -34,7 +34,7 @@ * the property values and constructor argument values contained in them, * resolving bean metadata values. * - *

Used by {@link PropertyPlaceholderConfigurer} to parse all String values + *

Used by {@link PlaceholderConfigurerSupport} to parse all String values * contained in a BeanDefinition, resolving any placeholders found. * * @author Juergen Hoeller @@ -43,7 +43,7 @@ * @see BeanDefinition * @see BeanDefinition#getPropertyValues * @see BeanDefinition#getConstructorArgumentValues - * @see PropertyPlaceholderConfigurer + * @see PlaceholderConfigurerSupport */ public class BeanDefinitionVisitor { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanExpressionContext.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanExpressionContext.java index 3e0b4000bb97..d3cadc37267a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanExpressionContext.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanExpressionContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,7 @@ public Object getObject(String key) { if (this.beanFactory.containsBean(key)) { return this.beanFactory.getBean(key); } - else if (this.scope != null){ + else if (this.scope != null) { return this.scope.resolveContextualObject(key); } else { 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 a4f4b6b733d8..ba1c0901bdb7 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 @@ -194,9 +194,9 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single * bean property values, constructor argument values, etc. *

This will override the default PropertyEditor mechanism and hence make * any custom editors or custom editor registrars irrelevant. + * @since 2.5 * @see #addPropertyEditorRegistrar * @see #registerCustomEditor - * @since 2.5 */ void setTypeConverter(TypeConverter typeConverter); 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 43189476678d..feb7c8a02bd8 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-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. @@ -16,10 +16,10 @@ package org.springframework.beans.factory.config; +import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -43,9 +43,9 @@ */ public class ConstructorArgumentValues { - private final Map indexedArgumentValues = new LinkedHashMap<>(0); + private final Map indexedArgumentValues = new LinkedHashMap<>(); - private final List genericArgumentValues = new LinkedList<>(); + private final List genericArgumentValues = new ArrayList<>(); /** @@ -484,7 +484,6 @@ public ValueHolder(@Nullable Object value, @Nullable String type, @Nullable Stri /** * Set the value for the constructor argument. - * @see PropertyPlaceholderConfigurer */ public void setValue(@Nullable Object value) { this.value = value; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/CustomEditorConfigurer.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/CustomEditorConfigurer.java index 42b2efd696c1..aeaf23fc041a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/CustomEditorConfigurer.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/CustomEditorConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,10 +68,10 @@ * *

* Note, that you shouldn't register {@link PropertyEditor} bean instances via - * the {@code customEditors} property as {@link PropertyEditor}s are stateful + * the {@code customEditors} property as {@link PropertyEditor PropertyEditors} are stateful * and the instances will then have to be synchronized for every editing * attempt. In case you need control over the instantiation process of - * {@link PropertyEditor}s, use a {@link PropertyEditorRegistrar} to register + * {@link PropertyEditor PropertyEditors}, use a {@link PropertyEditorRegistrar} to register * them. * *

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 995c0906c985..a07c81646cef 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-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. @@ -38,7 +38,9 @@ import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.ResolvableType; +import org.springframework.core.convert.TypeDescriptor; import org.springframework.lang.Nullable; +import org.springframework.util.ObjectUtils; /** * Descriptor for a specific dependency that is about to be injected. @@ -74,7 +76,10 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable private Class containingClass; @Nullable - private volatile ResolvableType resolvableType; + private transient volatile ResolvableType resolvableType; + + @Nullable + private transient volatile TypeDescriptor typeDescriptor; /** @@ -167,7 +172,8 @@ public boolean isRequired() { if (this.field != null) { return !(this.field.getType() == Optional.class || hasNullableAnnotation() || - (KotlinDetector.isKotlinType(this.field.getDeclaringClass()) && + (KotlinDetector.isKotlinReflectPresent() && + KotlinDetector.isKotlinType(this.field.getDeclaringClass()) && KotlinDelegate.isNullable(this.field))); } else { @@ -197,6 +203,24 @@ public boolean isEager() { return this.eager; } + /** + * Resolve the specified not-unique scenario: by default, + * throwing a {@link NoUniqueBeanDefinitionException}. + *

Subclasses may override this to select one of the instances or + * to opt out with no result at all through returning {@code null}. + * @param type the requested bean type + * @param matchingBeans a map of bean names and corresponding bean + * instances which have been pre-selected for the given type + * (qualifiers etc already applied) + * @return a bean instance to proceed with, or {@code null} for none + * @throws BeansException in case of the not-unique scenario being fatal + * @since 5.1 + */ + @Nullable + public Object resolveNotUnique(ResolvableType type, Map matchingBeans) throws BeansException { + throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet()); + } + /** * Resolve the specified not-unique scenario: by default, * throwing a {@link NoUniqueBeanDefinitionException}. @@ -209,7 +233,9 @@ public boolean isEager() { * @return a bean instance to proceed with, or {@code null} for none * @throws BeansException in case of the not-unique scenario being fatal * @since 4.3 + * @deprecated as of 5.1, in favor of {@link #resolveNotUnique(ResolvableType, Map)} */ + @Deprecated @Nullable public Object resolveNotUnique(Class type, Map matchingBeans) throws BeansException { throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet()); @@ -279,7 +305,7 @@ public void setContainingClass(Class containingClass) { } /** - * Build a ResolvableType object for the wrapped parameter/field. + * Build a {@link ResolvableType} object for the wrapped parameter/field. * @since 4.0 */ public ResolvableType getResolvableType() { @@ -293,10 +319,25 @@ public ResolvableType getResolvableType() { return resolvableType; } + /** + * Build a {@link TypeDescriptor} object for the wrapped parameter/field. + * @since 5.1.4 + */ + public TypeDescriptor getTypeDescriptor() { + TypeDescriptor typeDescriptor = this.typeDescriptor; + if (typeDescriptor == null) { + typeDescriptor = (this.field != null ? + new TypeDescriptor(getResolvableType(), getDependencyType(), getAnnotations()) : + new TypeDescriptor(obtainMethodParameter())); + this.typeDescriptor = typeDescriptor; + } + return typeDescriptor; + } + /** * Return whether a fallback match is allowed. *

This is {@code false} by default but may be overridden to return {@code true} in order - * to suggest to a {@link org.springframework.beans.factory.support.AutowireCandidateResolver} + * to suggest to an {@link org.springframework.beans.factory.support.AutowireCandidateResolver} * that a fallback match is acceptable as well. * @since 4.0 */ @@ -352,7 +393,6 @@ public Class getDependencyType() { Type[] args = ((ParameterizedType) type).getActualTypeArguments(); type = args[args.length - 1]; } - // TODO: Object.class if unresolvable } if (type instanceof Class) { return (Class) type; @@ -388,6 +428,11 @@ public boolean equals(Object other) { this.nestingLevel == otherDesc.nestingLevel && this.containingClass == otherDesc.containingClass); } + @Override + public int hashCode() { + return 31 * super.hashCode() + ObjectUtils.nullSafeHashCode(this.containingClass); + } + //--------------------------------------------------------------------- // Serialization support diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DestructionAwareBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DestructionAwareBeanPostProcessor.java index debe15893a2c..085f03817d59 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DestructionAwareBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DestructionAwareBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author 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,32 +30,25 @@ public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor { /** - * Apply this BeanPostProcessor to the given bean instance before - * its destruction. Can invoke custom destruction callbacks. - *

Like DisposableBean's {@code destroy} and a custom destroy method, - * this callback just applies to singleton beans in the factory (including - * inner beans). + * Apply this BeanPostProcessor to the given bean instance before its + * destruction, e.g. invoking custom destruction callbacks. + *

Like DisposableBean's {@code destroy} and a custom destroy method, this + * callback will only apply to beans which the container fully manages the + * lifecycle for. This is usually the case for singletons and scoped beans. * @param bean the bean instance to be destroyed * @param beanName the name of the bean * @throws org.springframework.beans.BeansException in case of errors - * @see org.springframework.beans.factory.DisposableBean - * @see org.springframework.beans.factory.support.AbstractBeanDefinition#setDestroyMethodName + * @see org.springframework.beans.factory.DisposableBean#destroy() + * @see org.springframework.beans.factory.support.AbstractBeanDefinition#setDestroyMethodName(String) */ void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException; /** * Determine whether the given bean instance requires destruction by this * post-processor. - *

NOTE: Even as a late addition, this method has been introduced on - * {@code DestructionAwareBeanPostProcessor} itself instead of on a SmartDABPP - * subinterface. This allows existing {@code DestructionAwareBeanPostProcessor} - * implementations to easily provide {@code requiresDestruction} logic while - * retaining compatibility with Spring <4.3, and it is also an easier onramp to - * declaring {@code requiresDestruction} as a Java 8 default method in Spring 5. - *

If an implementation of {@code DestructionAwareBeanPostProcessor} does - * not provide a concrete implementation of this method, Spring's invocation - * mechanism silently assumes a method returning {@code true} (the effective - * default before 4.3, and the to-be-default in the Java 8 method in Spring 5). + *

The default implementation returns {@code true}. If a pre-5 implementation + * of {@code DestructionAwareBeanPostProcessor} does not provide a concrete + * implementation of this method, Spring silently assumes {@code true} as well. * @param bean the bean instance to check * @return {@code true} if {@link #postProcessBeforeDestruction} is supposed to * be called for this bean instance eventually, or {@code false} if not needed diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/FieldRetrievingFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/FieldRetrievingFactoryBean.java index c537ee2f85b2..99e21c4582f7 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/FieldRetrievingFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/FieldRetrievingFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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,14 +35,15 @@ * *

Typically used for retrieving public static final constants. Usage example: * - *

// standard definition for exposing a static field, specifying the "staticField" property
+ * 
+ * // standard definition for exposing a static field, specifying the "staticField" property
  * <bean id="myField" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
  *   <property name="staticField" value="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
  * </bean>
  *
  * // convenience version that specifies a static field pattern as bean name
  * <bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
- *       class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
+ * class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/> *
* *

If you are using Spring 2.0, you can also use the following style of configuration for @@ -96,7 +97,7 @@ public void setTargetClass(@Nullable Class targetClass) { */ @Nullable public Class getTargetClass() { - return targetClass; + return this.targetClass; } /** @@ -202,7 +203,7 @@ else if (this.targetField == null) { } // Try to get the exact method first. - Class targetClass = (this.targetObject != null) ? this.targetObject.getClass() : this.targetClass; + Class targetClass = (this.targetObject != null ? this.targetObject.getClass() : this.targetClass); this.fieldObject = targetClass.getField(this.targetField); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java index 15b162677ab3..525ecebea59d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,7 +55,7 @@ public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { * {@link #postProcessAfterInitialization} callback from the configured * {@link BeanPostProcessor BeanPostProcessors}. *

This callback will only be applied to bean definitions with a bean class. - * In particular, it will not be applied to beans with a "factory-method". + * In particular, it will not be applied to beans with a factory method. *

Post-processors may implement the extended * {@link SmartInstantiationAwareBeanPostProcessor} interface in order * to predict the type of the bean object that they are going to return here. @@ -65,8 +65,8 @@ public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { * @return the bean object to expose instead of a default instance of the target bean, * or {@code null} to proceed with default instantiation * @throws org.springframework.beans.BeansException in case of errors + * @see #postProcessAfterInstantiation * @see org.springframework.beans.factory.support.AbstractBeanDefinition#hasBeanClass - * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getFactoryMethodName */ @Nullable default Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { @@ -86,11 +86,37 @@ default Object postProcessBeforeInstantiation(Class beanClass, String beanNam * Returning {@code false} will also prevent any subsequent InstantiationAwareBeanPostProcessor * instances being invoked on this bean instance. * @throws org.springframework.beans.BeansException in case of errors + * @see #postProcessBeforeInstantiation */ default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { return true; } + /** + * Post-process the given property values before the factory applies them + * to the given bean, without any need for property descriptors. + *

Implementations should return {@code null} (the default) if they provide a custom + * {@link #postProcessPropertyValues} implementation, and {@code pvs} otherwise. + * In a future version of this interface (with {@link #postProcessPropertyValues} removed), + * the default implementation will return the given {@code pvs} as-is directly. + * @param pvs the property values that the factory is about to apply (never {@code null}) + * @param bean the bean instance created, but whose properties have not yet been set + * @param beanName the name of the bean + * @return the actual property values to apply to the given bean (can be the passed-in + * PropertyValues instance), or {@code null} which proceeds with the existing properties + * but specifically continues with a call to {@link #postProcessPropertyValues} + * (requiring initialized {@code PropertyDescriptor}s for the current bean class) + * @throws org.springframework.beans.BeansException in case of errors + * @since 5.1 + * @see #postProcessPropertyValues + */ + @Nullable + default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) + throws BeansException { + + return null; + } + /** * Post-process the given property values before the factory applies them * to the given bean. Allows for checking whether all dependencies have been @@ -104,12 +130,14 @@ default boolean postProcessAfterInstantiation(Object bean, String beanName) thro * dependency types - which the factory handles specifically - already filtered out) * @param bean the bean instance created, but whose properties have not yet been set * @param beanName the name of the bean - * @return the actual property values to apply to the given bean - * (can be the passed-in PropertyValues instance), or {@code null} - * to skip property population + * @return the actual property values to apply to the given bean (can be the passed-in + * PropertyValues instance), or {@code null} to skip property population * @throws org.springframework.beans.BeansException in case of errors + * @see #postProcessProperties * @see org.springframework.beans.MutablePropertyValues + * @deprecated as of 5.1, in favor of {@link #postProcessProperties(PropertyValues, Object, String)} */ + @Deprecated @Nullable default PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { 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 abe387968c9d..993056aec8c6 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-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,7 @@ public abstract class InstantiationAwareBeanPostProcessorAdapter implements Smar @Override @Nullable - public Class predictBeanType(Class beanClass, String beanName) { + public Class predictBeanType(Class beanClass, String beanName) throws BeansException { return null; } @@ -68,6 +68,14 @@ public boolean postProcessAfterInstantiation(Object bean, String beanName) throw 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 { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/MethodInvokingFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/MethodInvokingFactoryBean.java index f3d1243f23aa..8885daee6fcf 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/MethodInvokingFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/MethodInvokingFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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 class MethodInvokingFactoryBean extends MethodInvokingBean implements Fac private boolean initialized = false; - /** Method call result in the singleton case */ + /** Method call result in the singleton case. */ @Nullable private Object singletonObject; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/NamedBeanHolder.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/NamedBeanHolder.java index 03981b123210..74ab18ffa895 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/NamedBeanHolder.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/NamedBeanHolder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author 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 @@ * * @author Juergen Hoeller * @since 4.3.3 + * @param the bean type * @see AutowireCapableBeanFactory#resolveNamedBean(Class) */ public class NamedBeanHolder implements NamedBean { 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 89da05a4a1e2..c9d0f8286942 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-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,10 +36,10 @@ * 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>
  * 
* * Example properties file: @@ -89,23 +89,23 @@ public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfigurer implements BeanNameAware, BeanFactoryAware { - /** Default placeholder prefix: {@value} */ + /** Default placeholder prefix: {@value}. */ public static final String DEFAULT_PLACEHOLDER_PREFIX = "${"; - /** Default placeholder suffix: {@value} */ + /** Default placeholder suffix: {@value}. */ public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}"; - /** Default value separator: {@value} */ + /** Default value separator: {@value}. */ public static final String DEFAULT_VALUE_SEPARATOR = ":"; - /** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX} */ + /** Defaults to {@value #DEFAULT_PLACEHOLDER_PREFIX}. */ protected String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX; - /** Defaults to {@value #DEFAULT_PLACEHOLDER_SUFFIX} */ + /** Defaults to {@value #DEFAULT_PLACEHOLDER_SUFFIX}. */ protected String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX; - /** Defaults to {@value #DEFAULT_VALUE_SEPARATOR} */ + /** Defaults to {@value #DEFAULT_VALUE_SEPARATOR}. */ @Nullable protected String valueSeparator = DEFAULT_VALUE_SEPARATOR; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/PreferencesPlaceholderConfigurer.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/PreferencesPlaceholderConfigurer.java index bfdaea481001..3cfffcb1d5eb 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/PreferencesPlaceholderConfigurer.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/PreferencesPlaceholderConfigurer.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. @@ -42,7 +42,9 @@ * @see #setSystemTreePath * @see #setUserTreePath * @see java.util.prefs.Preferences + * @deprecated as of 5.2, along with {@link PropertyPlaceholderConfigurer} */ +@Deprecated public class PreferencesPlaceholderConfigurer extends PropertyPlaceholderConfigurer implements InitializingBean { @Nullable @@ -121,7 +123,7 @@ protected String resolvePlaceholder(String placeholder, Properties props) { @Nullable protected String resolvePlaceholder(@Nullable String path, String key, Preferences preferences) { if (path != null) { - // Do not create the node if it does not exist... + // Do not create the node if it does not exist... try { if (preferences.nodeExists(path)) { return preferences.node(path).get(key, null); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyOverrideConfigurer.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyOverrideConfigurer.java index f89593c522e1..e94b0b08393a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyOverrideConfigurer.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyOverrideConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,6 +64,9 @@ */ public class PropertyOverrideConfigurer extends PropertyResourceConfigurer { + /** + * The default bean name separator. + */ public static final String DEFAULT_BEAN_NAME_SEPARATOR = "."; @@ -72,7 +75,7 @@ public class PropertyOverrideConfigurer extends PropertyResourceConfigurer { private boolean ignoreInvalidKeys = false; /** - * Contains names of beans that have overrides + * Contains names of beans that have overrides. */ private final Set beanNames = Collections.newSetFromMap(new ConcurrentHashMap<>(16)); @@ -129,7 +132,7 @@ protected void processKey(ConfigurableListableBeanFactory factory, String key, S "': expected 'beanName" + this.beanNameSeparator + "property'"); } String beanName = key.substring(0, separatorIndex); - String beanProperty = key.substring(separatorIndex+1); + String beanProperty = key.substring(separatorIndex + 1); this.beanNames.add(beanName); applyPropertyValue(factory, beanName, beanProperty, value); if (logger.isDebugEnabled()) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java index 34fe7ec5f5ba..aeeaf421a697 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.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. @@ -28,40 +28,32 @@ import org.springframework.util.StringValueResolver; /** - * {@link PlaceholderConfigurerSupport} subclass that resolves ${...} placeholders - * against {@link #setLocation local} {@link #setProperties properties} and/or system properties + * {@link PlaceholderConfigurerSupport} subclass that resolves ${...} placeholders against + * {@link #setLocation local} {@link #setProperties properties} and/or system properties * and environment variables. * - *

As of Spring 3.1, {@link org.springframework.context.support.PropertySourcesPlaceholderConfigurer - * PropertySourcesPlaceholderConfigurer} should be used preferentially over this implementation; it is - * more flexible through taking advantage of the {@link org.springframework.core.env.Environment Environment} and - * {@link org.springframework.core.env.PropertySource PropertySource} mechanisms also made available in Spring 3.1. - * *

{@link PropertyPlaceholderConfigurer} is still appropriate for use when: *

    *
  • the {@code spring-context} module is not available (i.e., one is using Spring's * {@code BeanFactory} API as opposed to {@code ApplicationContext}). - *
  • existing configuration makes use of the {@link #setSystemPropertiesMode(int) "systemPropertiesMode"} and/or - * {@link #setSystemPropertiesModeName(String) "systemPropertiesModeName"} properties. Users are encouraged to move - * away from using these settings, and rather configure property source search order through the container's - * {@code Environment}; however, exact preservation of functionality may be maintained by continuing to - * use {@code PropertyPlaceholderConfigurer}. + *
  • existing configuration makes use of the {@link #setSystemPropertiesMode(int) "systemPropertiesMode"} + * and/or {@link #setSystemPropertiesModeName(String) "systemPropertiesModeName"} properties. + * Users are encouraged to move away from using these settings, and rather configure property + * source search order through the container's {@code Environment}; however, exact preservation + * of functionality may be maintained by continuing to use {@code PropertyPlaceholderConfigurer}. *
* - *

Prior to Spring 3.1, the {@code } namespace element - * registered an instance of {@code PropertyPlaceholderConfigurer}. It will still do so if - * using the {@code spring-context-3.0.xsd} definition of the namespace. That is, you can preserve - * registration of {@code PropertyPlaceholderConfigurer} through the namespace, even if using Spring 3.1; - * simply do not update your {@code xsi:schemaLocation} and continue using the 3.0 XSD. - * * @author Juergen Hoeller * @author Chris Beams * @since 02.10.2003 * @see #setSystemPropertiesModeName * @see PlaceholderConfigurerSupport * @see PropertyOverrideConfigurer - * @see org.springframework.context.support.PropertySourcesPlaceholderConfigurer + * @deprecated as of 5.2; use {@code org.springframework.context.support.PropertySourcesPlaceholderConfigurer} + * instead which is more flexible through taking advantage of the {@link org.springframework.core.env.Environment} + * and {@link org.springframework.core.env.PropertySource} mechanisms. */ +@Deprecated public class PropertyPlaceholderConfigurer extends PlaceholderConfigurerSupport { /** Never check system properties. */ @@ -92,7 +84,6 @@ public class PropertyPlaceholderConfigurer extends PlaceholderConfigurerSupport * Set the system property mode by the name of the corresponding constant, * e.g. "SYSTEM_PROPERTIES_MODE_OVERRIDE". * @param constantName name of the constant - * @throws java.lang.IllegalArgumentException if an invalid constant was specified * @see #setSystemPropertiesMode */ public void setSystemPropertiesModeName(String constantName) throws IllegalArgumentException { @@ -124,14 +115,9 @@ public void setSystemPropertiesMode(int systemPropertiesMode) { * against system environment variables. Note that it is generally recommended * to pass external values in as JVM system properties: This can easily be * achieved in a startup script, even for existing environment variables. - *

NOTE: Access to environment variables does not work on the - * Sun VM 1.4, where the corresponding {@link System#getenv} support was - * disabled - before it eventually got re-enabled for the Sun VM 1.5. - * Please upgrade to 1.5 (or higher) if you intend to rely on the - * environment variable support. * @see #setSystemPropertiesMode - * @see java.lang.System#getProperty(String) - * @see java.lang.System#getenv(String) + * @see System#getProperty(String) + * @see System#getenv(String) */ public void setSearchSystemEnvironment(boolean searchSystemEnvironment) { this.searchSystemEnvironment = searchSystemEnvironment; @@ -250,7 +236,7 @@ public String resolveStringValue(String strVal) throws BeansException { } - private class PropertyPlaceholderConfigurerResolver implements PlaceholderResolver { + private final class PropertyPlaceholderConfigurerResolver implements PlaceholderResolver { private final Properties props; @@ -261,7 +247,8 @@ private PropertyPlaceholderConfigurerResolver(Properties props) { @Override @Nullable public String resolvePlaceholder(String placeholderName) { - return PropertyPlaceholderConfigurer.this.resolvePlaceholder(placeholderName, props, systemPropertiesMode); + return PropertyPlaceholderConfigurer.this.resolvePlaceholder(placeholderName, + this.props, systemPropertiesMode); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/TypedStringValue.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/TypedStringValue.java index fb3a8f385cdd..5fda7a72de8c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/TypedStringValue.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/TypedStringValue.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. @@ -87,7 +87,6 @@ public TypedStringValue(@Nullable String value, String targetTypeName) { * Set the String value. *

Only necessary for manipulating a registered value, * for example in BeanFactoryPostProcessors. - * @see PropertyPlaceholderConfigurer */ public void setValue(@Nullable String value) { this.value = value; @@ -105,7 +104,6 @@ public String getValue() { * Set the type to convert to. *

Only necessary for manipulating a registered value, * for example in BeanFactoryPostProcessors. - * @see PropertyPlaceholderConfigurer */ public void setTargetType(Class targetType) { Assert.notNull(targetType, "'targetType' must not be null"); 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 baf243bffedd..40413554f543 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-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. @@ -63,7 +63,7 @@ public abstract class YamlProcessor { /** * A map of document matchers allowing callers to selectively use only * some of the documents in a YAML resource. In YAML documents are - * separated by --- lines, and each document is converted + * separated by {@code ---} lines, and each document is converted * to properties before the match is made. E.g. *

 	 * environment: dev
@@ -75,15 +75,16 @@ public abstract class YamlProcessor {
 	 * name: My Cool App
 	 * 
* when mapped with - * documentMatchers = YamlProcessor.mapMatcher({"environment": "prod"}) + *
+	 * setDocumentMatchers(properties ->
+	 *     ("prod".equals(properties.getProperty("environment")) ? MatchStatus.FOUND : MatchStatus.NOT_FOUND));
+	 * 
* would end up as *
 	 * environment=prod
 	 * url=http://foo.bar.com
 	 * name=My Cool App
-	 * url=http://dev.bar.com
 	 * 
- * @param matchers a map of keys to value patterns (regular expressions) */ public void setDocumentMatchers(DocumentMatcher... matchers) { this.documentMatchers = Arrays.asList(matchers); @@ -92,8 +93,7 @@ public void setDocumentMatchers(DocumentMatcher... matchers) { /** * Flag indicating that a document for which all the * {@link #setDocumentMatchers(DocumentMatcher...) document matchers} abstain will - * nevertheless match. - * @param matchDefault the flag to set (default true) + * nevertheless match. Default is {@code true}. */ public void setMatchDefault(boolean matchDefault) { this.matchDefault = matchDefault; @@ -102,9 +102,7 @@ public void setMatchDefault(boolean matchDefault) { /** * Method to use for resolving resources. Each resource will be converted to a Map, * so this property is used to decide which map entries to keep in the final output - * from this factory. - * @param resolutionMethod the resolution method to set (defaults to - * {@link ResolutionMethod#OVERRIDE}). + * from this factory. Default is {@link ResolutionMethod#OVERRIDE}. */ public void setResolutionMethod(ResolutionMethod resolutionMethod) { Assert.notNull(resolutionMethod, "ResolutionMethod must not be null"); @@ -158,8 +156,7 @@ private boolean process(MatchCallback callback, Yaml yaml, Resource resource) { if (logger.isDebugEnabled()) { logger.debug("Loading from YAML: " + resource); } - Reader reader = new UnicodeReader(resource.getInputStream()); - try { + try (Reader reader = new UnicodeReader(resource.getInputStream())) { for (Object object : yaml.loadAll(reader)) { if (object != null && process(asMap(object), callback)) { count++; @@ -173,9 +170,6 @@ private boolean process(MatchCallback callback, Yaml yaml, Resource resource) { " from YAML resource: " + resource); } } - finally { - reader.close(); - } } catch (IOException ex) { handleProcessError(resource, ex); @@ -298,7 +292,8 @@ else if (value instanceof Collection) { Collection collection = (Collection) value; if (collection.isEmpty()) { result.put(key, ""); - } else { + } + else { int count = 0; for (Object object : collection) { buildFlattenedMap(result, Collections.singletonMap( @@ -344,7 +339,7 @@ public interface DocumentMatcher { /** - * Status returned from {@link DocumentMatcher#matches(java.util.Properties)} + * Status returned from {@link DocumentMatcher#matches(java.util.Properties)}. */ public enum MatchStatus { 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 e9435b8349bf..4581f9214151 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 @@ -236,6 +236,10 @@ public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefin return this.standardXmlBeanDefinitionReader.loadBeanDefinitions(encodedResource); } + if (logger.isTraceEnabled()) { + logger.trace("Loading Groovy bean definitions from " + encodedResource); + } + Closure beans = new Closure(this) { @Override public Object call(Object[] args) { @@ -265,7 +269,12 @@ public void setVariable(String name, Object value) { throw new BeanDefinitionParsingException(new Problem("Error evaluating Groovy script: " + ex.getMessage(), new Location(encodedResource.getResource()), null, ex)); } - return getRegistry().getBeanDefinitionCount() - countBefore; + + int count = getRegistry().getBeanDefinitionCount() - countBefore; + if (logger.isDebugEnabled()) { + logger.debug("Loaded " + count + " bean definitions from " + encodedResource); + } + return count; } @@ -371,9 +380,9 @@ public Object invokeMethod(String name, Object arg) { } else if ("ref".equals(name)) { String refName; - if (args[0] == null) + if (args[0] == null) { throw new IllegalArgumentException("Argument to ref() is not a valid bean or was not found"); - + } if (args[0] instanceof RuntimeBeanReference) { refName = ((RuntimeBeanReference) args[0]).getBeanName(); } @@ -489,11 +498,11 @@ else if (args[0] instanceof Map) { Map.Entry factoryBeanEntry = (Map.Entry) ((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; + int constructorArgsTest = (hasClosureArgument ? 2 : 1); // If we have more than this number of args, we have constructor args if (args.length > constructorArgsTest){ // factory-method requires args - int endOfConstructArgs = (hasClosureArgument? args.length - 1 : args.length); + int endOfConstructArgs = (hasClosureArgument ? args.length - 1 : args.length); this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null, resolveConstructorArguments(args, 1, endOfConstructArgs)); } @@ -511,7 +520,7 @@ else if (args[0] instanceof Closure) { } else { List constructorArgs = resolveConstructorArguments(args, 0, hasClosureArgument ? args.length - 1 : args.length); - currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null, constructorArgs); + this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null, constructorArgs); } if (hasClosureArgument) { @@ -545,8 +554,8 @@ else if (constructorArgs[i] instanceof Map){ } /** - * Checks whether there are any {@link RuntimeBeanReference}s inside the {@link Map} - * and converts it to a {@link ManagedMap} if necessary. + * Checks whether there are any {@link RuntimeBeanReference RuntimeBeanReferences} + * inside the {@link Map} and converts it to a {@link ManagedMap} if necessary. * @param map the original Map * @return either the original map or a managed copy of it */ @@ -567,8 +576,8 @@ private Object manageMapIfNecessary(Map map) { } /** - * Checks whether there are any {@link RuntimeBeanReference}s inside the {@link List} - * and converts it to a {@link ManagedList} if necessary. + * Checks whether there are any {@link RuntimeBeanReference RuntimeBeanReferences} + * inside the {@link List} and converts it to a {@link ManagedList} if necessary. * @param list the original List * @return either the original list or a managed copy of it */ @@ -630,7 +639,7 @@ else if (value instanceof Closure) { /** * This method overrides property retrieval in the scope of the - * {@code GroovyBeanDefinitionReader} to either: + * {@code GroovyBeanDefinitionReader}. A property retrieval will either: *