diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2538a2c296..f87099b85f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,14 +29,6 @@ do not find something similar, please create a new JIRA issue before submitting a pull request unless the change is truly trivial -- for example: typo fixes, removing compiler warnings, etc. -### Discuss non-trivial contribution ideas with committers - -If you're considering anything more than correcting a typo or fixing a minor -bug, please discuss it on the [spring-framework-contrib][] mailing list before -submitting a pull request. We're happy to provide guidance, but please spend an -hour or two researching the subject on your own, including searching the mailing -list for prior discussions. - ### Sign the Individual Contributor License Agreement (ICLA) If you have not previously done so, please fill out and submit the diff --git a/build.gradle b/build.gradle index 7962a9d2d5..dd68ea7f8e 100644 --- a/build.gradle +++ b/build.gradle @@ -27,57 +27,52 @@ configure(allprojects) { project -> group = "org.springframework" version = qualifyVersionIfNecessary(version) - ext.aspectjVersion = "1.8.7" + ext.aspectjVersion = "1.8.9" ext.eclipselinkVersion = "2.4.2" - ext.ehcacheVersion = "2.10.1" + ext.ehcacheVersion = "2.10.3" ext.ehcachejcacheVersion = "1.0.1" - ext.ehcache3Version = "3.0.0.m4" - ext.ejbApiVersion = "3.0" - ext.fileuploadVersion = "1.3.1" + ext.ehcache3Version = "3.0.3" + ext.ejbVersion = "3.0" + ext.fileuploadVersion = "1.3.2" ext.freemarkerVersion = "2.3.23" - ext.groovyVersion = "2.4.5" - ext.gsonVersion = "2.5" + ext.groovyVersion = "2.4.7" + ext.gsonVersion = "2.6.2" ext.guavaVersion = "19.0" ext.hamcrestVersion = "1.3" ext.hibernate3Version = "3.6.10.Final" ext.hibernate4Version = "4.3.11.Final" - ext.hibernate5Version = "5.0.5.Final" + ext.hibernate5Version = "5.0.11.Final" ext.hibval4Version = "4.3.2.Final" - ext.hibval5Version = "5.2.2.Final" - ext.hsqldbVersion = "2.3.3" - ext.htmlunitVersion = "2.19" + ext.hibval5Version = "5.2.4.Final" + ext.hsqldbVersion = "2.3.4" ext.httpasyncVersion = "4.1.1" - ext.httpclientVersion = "4.5.1" - ext.jackson2Version = "2.6.4" - ext.jasperreportsVersion = "6.2.0" - ext.javamailVersion = "1.5.4" - ext.jettyVersion = "9.3.6.v20151106" - ext.jodaVersion = "2.9.1" - ext.jrubyVersion = "1.7.23" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) - ext.jsonassertVersion = "1.2.3" - ext.jsonpathVersion = "2.1.0" + ext.httpclientVersion = "4.5.2" + ext.jackson2Version = "2.6.7" + ext.jasperreportsVersion = "6.2.1" + ext.javamailVersion = "1.5.5" + ext.jettyVersion = "9.3.9.v20160517" + ext.jodaVersion = "2.9.6" + ext.jrubyVersion = "1.7.26" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) ext.jtaVersion = "1.2" ext.junitVersion = "4.12" - ext.nettyVersion = "4.0.33.Final" - ext.okhttpVersion = "2.7.0" - ext.openjpaVersion = "2.4.0" + ext.log4jVersion = "1.2.17" + ext.nettyVersion = "4.0.42.Final" + ext.okhttpVersion = "2.7.5" + ext.openjpaVersion = "2.4.1" ext.poiVersion = "3.13" - ext.protobufVersion = "2.6.1" - ext.reactorVersion = "2.0.7.RELEASE" + ext.reactorVersion = "2.0.8.RELEASE" ext.romeVersion = "1.5.1" - ext.seleniumVersion = "2.48.2" - ext.slf4jVersion = "1.7.13" - ext.snakeyamlVersion = "1.16" + ext.slf4jVersion = "1.7.21" + ext.snakeyamlVersion = "1.17" ext.snifferVersion = "1.14" ext.testngVersion = "6.9.10" ext.tiles2Version = "2.2.2" ext.tiles3Version = "3.0.5" - ext.tomcatVersion = "8.0.30" + ext.tomcatVersion = "8.0.39" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support - ext.undertowVersion = "1.3.10.Final" - ext.woodstoxVersion = "5.0.1" + ext.undertowVersion = "1.3.25.Final" ext.xmlunitVersion = "1.6" - ext.xstreamVersion = "1.4.8" + ext.xstreamVersion = "1.4.9" ext.gradleScriptDir = "${rootProject.projectDir}/gradle" @@ -130,8 +125,6 @@ configure(allprojects) { project -> repositories { maven { url "https://repo.spring.io/libs-release" } - maven { url "https://repo.spring.io/milestone" } - maven { url "https://repo.spring.io/snapshot" } // reactor 2.0.6 snapshot } dependencies { @@ -200,7 +193,7 @@ configure(allprojects) { project -> "http://aopalliance.sourceforge.net/doc/", "http://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/", "http://ehcache.org/apidocs/", - "http://quartz-scheduler.org/api/2.2.0/", + "http://quartz-scheduler.org/api/2.2.1/", "http://fasterxml.github.com/jackson-core/javadoc/2.3.0/", "http://fasterxml.github.com/jackson-databind/javadoc/2.3.0/", "http://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.3.0/", @@ -291,7 +284,7 @@ project("spring-core") { // both into the spring-core jar. cglib 3.2 itself depends on asm 5.0 and is therefore // further transformed by the JarJar task to depend on org.springframework.asm; this // avoids including two different copies of asm unnecessarily. - def cglibVersion = "3.2.0" + def cglibVersion = "3.2.4" def objenesisVersion = "2.2" configurations { @@ -353,10 +346,10 @@ project("spring-core") { optional("commons-codec:commons-codec:1.10") optional("org.aspectj:aspectjweaver:${aspectjVersion}") optional("net.sf.jopt-simple:jopt-simple:4.9") - optional("log4j:log4j:1.2.17") + optional("log4j:log4j:${log4jVersion}") testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}") testCompile("xmlunit:xmlunit:${xmlunitVersion}") - testCompile("com.fasterxml.woodstox:woodstox-core:${woodstoxVersion}") { + testCompile("com.fasterxml.woodstox:woodstox-core:5.0.3") { exclude group: "stax", module: "stax-api" } } @@ -384,9 +377,9 @@ project("spring-beans") { optional("javax.inject:javax.inject:1") optional("javax.el:javax.el-api:2.2.5") optional("org.yaml:snakeyaml:${snakeyamlVersion}") - testCompile("log4j:log4j:1.2.17") + testCompile("log4j:log4j:${log4jVersion}") testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}") - } + } } project("spring-beans-groovy") { @@ -477,7 +470,7 @@ project("spring-context") { compile(files(project(":spring-core").cglibRepackJar)) optional(project(":spring-instrument")) optional("javax.inject:javax.inject:1") - optional("javax.ejb:ejb-api:${ejbApiVersion}") + optional("javax.ejb:ejb-api:${ejbVersion}") optional("javax.enterprise.concurrent:javax.enterprise.concurrent-api:1.0") optional("javax.money:money-api:1.0") optional("org.eclipse.persistence:javax.persistence:2.0.0") @@ -531,7 +524,7 @@ project("spring-messaging") { testCompile("org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}") testCompile("io.netty:netty-all:${nettyVersion}") testCompile("commons-dbcp:commons-dbcp:1.4") - testCompile("log4j:log4j:1.2.17") + testCompile("log4j:log4j:${log4jVersion}") testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") testCompile("xmlunit:xmlunit:${xmlunitVersion}") } @@ -548,7 +541,7 @@ project("spring-tx") { optional("aopalliance:aopalliance:1.0") optional("javax.transaction:javax.transaction-api:${jtaVersion}") optional("javax.resource:connector-api:1.5") - optional("javax.ejb:ejb-api:${ejbApiVersion}") + optional("javax.ejb:ejb-api:${ejbVersion}") optional("com.ibm.websphere:uow:6.0.2.17") testCompile("org.aspectj:aspectjweaver:${aspectjVersion}") testCompile("org.eclipse.persistence:javax.persistence:2.0.0") @@ -566,15 +559,10 @@ project("spring-oxm") { targetCompatibility = 1.6 } - if (!System.getProperty("java.version").contains("1.8.")) { - // necessary because castor and xjc tasks cannot find the JDK's compiler on JDK 9 - compileTestJava.enabled = false - } - dependencies { compile(project(":spring-beans")) compile(project(":spring-core")) - optional("org.codehaus.castor:castor-xml:1.4.0") { + optional("org.codehaus.castor:castor-xml:1.4.1") { exclude group: 'stax', module: 'stax-api' exclude group: "org.springframework", module: "spring-context" } @@ -589,7 +577,7 @@ project("spring-oxm") { testCompile(project(":spring-context")) testCompile("xmlunit:xmlunit:${xmlunitVersion}") testCompile("xpp3:xpp3:1.1.4c") - testCompile("org.codehaus.jettison:jettison:1.3.7") { + testCompile("org.codehaus.jettison:jettison:1.3.8") { exclude group: 'stax', module: 'stax-api' } if (compileTestJava.enabled) { @@ -630,7 +618,7 @@ project("spring-jdbc") { optional("javax.transaction:javax.transaction-api:${jtaVersion}") optional("com.mchange:c3p0:0.9.5.2") optional("org.hsqldb:hsqldb:${hsqldbVersion}") - optional("com.h2database:h2:1.4.190") + optional("com.h2database:h2:1.4.193") optional("org.apache.derby:derby:10.12.1.1") optional("org.apache.derby:derbyclient:10.12.1.1") } @@ -649,7 +637,7 @@ project("spring-context-support") { optional("javax.cache:cache-api:1.0.0") optional("com.google.guava:guava:${guavaVersion}") optional("net.sf.ehcache:ehcache:${ehcacheVersion}") - optional("org.quartz-scheduler:quartz:2.2.2") + optional("org.quartz-scheduler:quartz:2.2.3") optional("org.codehaus.fabric3.api:commonj:1.1.0") optional("org.apache.velocity:velocity:1.7") optional("org.freemarker:freemarker:${freemarkerVersion}") @@ -722,9 +710,9 @@ project("spring-web") { optional("org.eclipse.jetty:jetty-server:${jettyVersion}") { exclude group: "javax.servlet", module: "javax.servlet-api" } - optional("log4j:log4j:1.2.17") + optional("log4j:log4j:${log4jVersion}") + optional("com.google.protobuf:protobuf-java:2.6.1") optional("com.googlecode.protobuf-java-format:protobuf-java-format:1.2") - optional("com.google.protobuf:protobuf-java:${protobufVersion}") optional("javax.mail:javax.mail-api:${javamailVersion}") testCompile(project(":spring-context-support")) // for JafMediaTypeFactory testCompile("xmlunit:xmlunit:${xmlunitVersion}") @@ -739,52 +727,6 @@ project("spring-web") { } } -project("spring-websocket") { - description = "Spring WebSocket" - - dependencies { - compile(project(":spring-core")) - compile(project(":spring-context")) - compile(project(":spring-web")) - optional(project(":spring-messaging")) - optional(project(":spring-webmvc")) - optional("javax.servlet:javax.servlet-api:3.1.0") - optional("javax.websocket:javax.websocket-api:1.0") - optional("org.apache.tomcat:tomcat-websocket:${tomcatVersion}") { - exclude group: "org.apache.tomcat", module: "tomcat-websocket-api" - exclude group: "org.apache.tomcat", module: "tomcat-servlet-api" - } - optional("org.glassfish.tyrus:tyrus-spi:${tyrusVersion}") - optional("org.glassfish.tyrus:tyrus-core:${tyrusVersion}") - optional("org.glassfish.tyrus:tyrus-server:${tyrusVersion}") - optional("org.glassfish.tyrus:tyrus-container-servlet:${tyrusVersion}") - optional("org.eclipse.jetty:jetty-webapp:${jettyVersion}") { - exclude group: "javax.servlet", module: "javax.servlet" - } - optional("org.eclipse.jetty.websocket:websocket-server:${jettyVersion}") { - exclude group: "javax.servlet", module: "javax.servlet" - } - optional("org.eclipse.jetty.websocket:websocket-client:${jettyVersion}") - optional("org.eclipse.jetty:jetty-client:${jettyVersion}") - optional("io.undertow:undertow-core:${undertowVersion}") - optional("io.undertow:undertow-servlet:${undertowVersion}") { - exclude group: "org.jboss.spec.javax.servlet", module: "jboss-servlet-api_3.1_spec" - exclude group: "org.jboss.spec.javax.annotation", module: "jboss-annotations-api_1.2_spec" - } - optional("io.undertow:undertow-websockets-jsr:${undertowVersion}") { - exclude group: "org.jboss.spec.javax.websocket", module: "jboss-websocket-api_1.1_spec" - } - optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}") - testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}") - testCompile("org.apache.tomcat.embed:tomcat-embed-websocket:${tomcatVersion}") - testCompile("org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}") - testCompile("io.projectreactor:reactor-net:${reactorVersion}") - testCompile("io.netty:netty-all:${nettyVersion}") - testCompile("log4j:log4j:1.2.17") - testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") - } -} - project("spring-orm") { description = "Spring Object/Relational Mapping" @@ -837,7 +779,7 @@ project("spring-orm-hibernate4") { optional("org.hibernate:hibernate-core:${hibernate4Version}") optional("org.hibernate:hibernate-entitymanager:${hibernate4Version}") optional("javax.servlet:javax.servlet-api:3.0.1") - optional("aopalliance:aopalliance:1.0") + optional("aopalliance:aopalliance:1.0") testCompile("javax.validation:validation-api:1.1.0.GA") testCompile("org.hibernate:hibernate-validator:${hibval5Version}") testCompile("javax.el:javax.el-api:2.2.5") @@ -913,9 +855,8 @@ project("spring-webmvc") { exclude group: "org.slf4j", module: "jcl-over-slf4j" exclude group: "org.springframework", module: "spring-web" } - optional('org.webjars:webjars-locator:0.28') + optional('org.webjars:webjars-locator:0.32') testCompile(project(":spring-aop")) - testCompile("rhino:js:1.7R1") testCompile("xmlunit:xmlunit:${xmlunitVersion}") testCompile("dom4j:dom4j:1.6.1") { exclude group: "xml-apis", module: "xml-apis" @@ -938,9 +879,10 @@ project("spring-webmvc") { testCompile("commons-io:commons-io:1.3") testCompile("joda-time:joda-time:${jodaVersion}") testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") - testCompile("org.jruby:jruby:${jrubyVersion}") - testCompile("org.python:jython-standalone:2.5.3") - testCompile("org.webjars:underscorejs:1.8.3") + testCompile("org.mozilla:rhino:1.7.7.1") + testRuntime("org.jruby:jruby:${jrubyVersion}") + testRuntime("org.python:jython-standalone:2.5.3") + testRuntime("org.webjars:underscorejs:1.8.3") } } @@ -991,6 +933,52 @@ project("spring-webmvc-portlet") { } } +project("spring-websocket") { + description = "Spring WebSocket" + + dependencies { + compile(project(":spring-core")) + compile(project(":spring-context")) + compile(project(":spring-web")) + optional(project(":spring-messaging")) + optional(project(":spring-webmvc")) + optional("javax.servlet:javax.servlet-api:3.1.0") + optional("javax.websocket:javax.websocket-api:1.0") + optional("org.apache.tomcat:tomcat-websocket:${tomcatVersion}") { + exclude group: "org.apache.tomcat", module: "tomcat-websocket-api" + exclude group: "org.apache.tomcat", module: "tomcat-servlet-api" + } + optional("org.glassfish.tyrus:tyrus-spi:${tyrusVersion}") + optional("org.glassfish.tyrus:tyrus-core:${tyrusVersion}") + optional("org.glassfish.tyrus:tyrus-server:${tyrusVersion}") + optional("org.glassfish.tyrus:tyrus-container-servlet:${tyrusVersion}") + optional("org.eclipse.jetty:jetty-webapp:${jettyVersion}") { + exclude group: "javax.servlet", module: "javax.servlet" + } + optional("org.eclipse.jetty.websocket:websocket-server:${jettyVersion}") { + exclude group: "javax.servlet", module: "javax.servlet" + } + optional("org.eclipse.jetty.websocket:websocket-client:${jettyVersion}") + optional("org.eclipse.jetty:jetty-client:${jettyVersion}") + optional("io.undertow:undertow-core:${undertowVersion}") + optional("io.undertow:undertow-servlet:${undertowVersion}") { + exclude group: "org.jboss.spec.javax.servlet", module: "jboss-servlet-api_3.1_spec" + exclude group: "org.jboss.spec.javax.annotation", module: "jboss-annotations-api_1.2_spec" + } + optional("io.undertow:undertow-websockets-jsr:${undertowVersion}") { + exclude group: "org.jboss.spec.javax.websocket", module: "jboss-websocket-api_1.1_spec" + } + optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}") + testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}") + testCompile("org.apache.tomcat.embed:tomcat-embed-websocket:${tomcatVersion}") + testCompile("org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}") + testCompile("io.projectreactor:reactor-net:${reactorVersion}") + testCompile("io.netty:netty-all:${nettyVersion}") + testCompile("log4j:log4j:${log4jVersion}") + testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") + } +} + project("spring-test") { description = "Spring TestContext Framework" @@ -1018,15 +1006,15 @@ project("spring-test") { optional("org.aspectj:aspectjweaver:${aspectjVersion}") optional("org.codehaus.groovy:groovy-all:${groovyVersion}") optional("org.hamcrest:hamcrest-core:${hamcrestVersion}") - optional("com.jayway.jsonpath:json-path:${jsonpathVersion}") - optional("org.skyscreamer:jsonassert:${jsonassertVersion}") optional("xmlunit:xmlunit:${xmlunitVersion}") - optional("net.sourceforge.htmlunit:htmlunit:${htmlunitVersion}") - optional("org.seleniumhq.selenium:selenium-htmlunit-driver:${seleniumVersion}") + optional("net.sourceforge.htmlunit:htmlunit:2.19") + optional("org.seleniumhq.selenium:selenium-htmlunit-driver:2.48.2") + optional("org.skyscreamer:jsonassert:1.2.3") + optional("com.jayway.jsonpath:json-path:2.1.0") testCompile(project(":spring-context-support")) testCompile(project(":spring-oxm")) testCompile("javax.mail:javax.mail-api:${javamailVersion}") - testCompile("javax.ejb:ejb-api:${ejbApiVersion}") + testCompile("javax.ejb:ejb-api:${ejbVersion}") testCompile("org.hibernate:hibernate-core:${hibernate4Version}") testCompile("org.hibernate:hibernate-entitymanager:${hibernate4Version}") testCompile("org.hibernate:hibernate-validator:${hibval5Version}") @@ -1044,6 +1032,7 @@ project("spring-test") { testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") testCompile("org.apache.httpcomponents:httpclient:${httpclientVersion}") testCompile("javax.cache:cache-api:1.0.0") + testRuntime("log4j:log4j:${log4jVersion}") testRuntime("org.ehcache:ehcache:${ehcache3Version}") testRuntime("org.terracotta:management-model:2.0.0") } @@ -1080,8 +1069,8 @@ project("spring-aspects") { dependencies { aspects(project(":spring-orm")) - ajc("org.aspectj:aspectjtools:1.9.0.BETA-2") // for the ability to build on JDK 9, not exposed in the POMs yet - rt("org.aspectj:aspectjrt:${aspectjVersion}") // regular AspectJ version here, to be exposed in the POMs + ajc("org.aspectj:aspectjtools:${aspectjVersion}") + rt("org.aspectj:aspectjrt:${aspectjVersion}") compile("org.aspectj:aspectjweaver:${aspectjVersion}") provided("org.eclipse.persistence:javax.persistence:2.0.0") optional(project(":spring-aop")) // for @Async support @@ -1099,8 +1088,7 @@ project("spring-aspects") { eclipse.project { natures += "org.eclipse.ajdt.ui.ajnature" - buildCommands = [new org.gradle.plugins.ide.eclipse.model. - BuildCommand("org.eclipse.ajdt.core.ajbuilder")] + buildCommands = [new org.gradle.plugins.ide.eclipse.model.BuildCommand("org.eclipse.ajdt.core.ajbuilder")] } } @@ -1170,9 +1158,7 @@ configure(rootProject) { separateOutputDirs = false backends = ['docbook'] options doctype: 'book', eruby: 'erubis' - attributes 'spring-version': project.version, - 'revnumber' : project.version, - 'docinfo' : "" + attributes 'spring-version': project.version, 'revnumber': project.version, 'docinfo': "" } reference { @@ -1308,7 +1294,7 @@ configure(rootProject) { baseName = "spring-framework" classifier = "dist" description = "Builds -${classifier} archive, containing all jars and docs, " + - "suitable for community download page." + "suitable for community download page." ext.baseDir = "${baseName}-${project.version}"; diff --git a/change.diff b/change.diff new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gradle.properties b/gradle.properties index d5fc1bea40..a09ae3bc47 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=4.2.4.BUILD-SNAPSHOT +version=4.2.10.BUILD-SNAPSHOT diff --git a/import-into-eclipse.bat b/import-into-eclipse.bat index 8ba6e975fe..807cd4f07f 100644 --- a/import-into-eclipse.bat +++ b/import-into-eclipse.bat @@ -29,8 +29,8 @@ 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 cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse" -SET COMMAND=gradlew cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse +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 ----------------------------------------------------------------------- @@ -69,7 +69,7 @@ echo When the above is complete, return here and press the enter key. pause -set COMMAND=gradlew :eclipse +set COMMAND=gradlew --no-daemon :eclipse echo. echo ----------------------------------------------------------------------- diff --git a/import-into-eclipse.sh b/import-into-eclipse.sh index c682fbc2a4..b87c12b63c 100755 --- a/import-into-eclipse.sh +++ b/import-into-eclipse.sh @@ -42,7 +42,7 @@ read # - generates metadata for all subprojects # - skips metadata gen for the root project (-x :eclipse) to work # around Eclipse's inability to import hierarchical project structures -COMMAND="./gradlew cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse" +COMMAND="./gradlew --no-daemon cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse" cat < targetClass, Object[] args); + boolean matches(Method method, Class targetClass, Object... args); /** 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 0b82427e8b..cf21e97c91 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-2012 the original author or authors. + * Copyright 2002-2015 the original author 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,12 +29,14 @@ class TrueMethodMatcher implements MethodMatcher, Serializable { public static final TrueMethodMatcher INSTANCE = new TrueMethodMatcher(); + /** * Enforce Singleton pattern. */ private TrueMethodMatcher() { } + @Override public boolean isRuntime() { return false; @@ -46,11 +48,17 @@ public boolean matches(Method method, Class targetClass) { } @Override - public boolean matches(Method method, Class targetClass, Object[] args) { + public boolean matches(Method method, Class targetClass, Object... args) { // Should never be invoked as isRuntime returns false. throw new UnsupportedOperationException(); } + + @Override + public String toString() { + return "MethodMatcher.TRUE"; + } + /** * Required to support serialization. Replaces with canonical * instance on deserialization, protecting Singleton pattern. @@ -60,9 +68,4 @@ private Object readResolve() { return INSTANCE; } - @Override - public String toString() { - return "MethodMatcher.TRUE"; - } - } 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 4d0392c247..71bd31d934 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 @@ -135,7 +135,7 @@ public static JoinPoint currentJoinPoint() { */ private int joinPointStaticPartArgumentIndex = -1; - private Map argumentBindings = null; + private Map argumentBindings; private boolean argumentsIntrospected = false; @@ -250,17 +250,17 @@ public void setArgumentNamesFromStringArray(String... args) { this.argumentNames[i] + "' that is not a valid Java identifier"); } } - if (argumentNames != null) { - if (aspectJAdviceMethod.getParameterTypes().length == argumentNames.length + 1) { + if (this.argumentNames != null) { + if (this.aspectJAdviceMethod.getParameterTypes().length == this.argumentNames.length + 1) { // May need to add implicit join point arg name... - Class firstArgType = aspectJAdviceMethod.getParameterTypes()[0]; + Class firstArgType = this.aspectJAdviceMethod.getParameterTypes()[0]; if (firstArgType == JoinPoint.class || firstArgType == ProceedingJoinPoint.class || firstArgType == JoinPoint.StaticPart.class) { - String[] oldNames = argumentNames; - argumentNames = new String[oldNames.length + 1]; - argumentNames[0] = "THIS_JOIN_POINT"; - System.arraycopy(oldNames, 0, argumentNames, 1, oldNames.length); + String[] oldNames = this.argumentNames; + this.argumentNames = new String[oldNames.length + 1]; + this.argumentNames[0] = "THIS_JOIN_POINT"; + System.arraycopy(oldNames, 0, this.argumentNames, 1, oldNames.length); } } } @@ -456,8 +456,8 @@ private void bindExplicitArguments(int numArgumentsLeftToBind) { int numExpectedArgumentNames = this.aspectJAdviceMethod.getParameterTypes().length; if (this.argumentNames.length != numExpectedArgumentNames) { - throw new IllegalStateException("Expecting to find " + numExpectedArgumentNames - + " arguments to bind by name in advice, but actually found " + + throw new IllegalStateException("Expecting to find " + numExpectedArgumentNames + + " arguments to bind by name in advice, but actually found " + this.argumentNames.length + " arguments."); } @@ -471,8 +471,8 @@ private void bindExplicitArguments(int numArgumentsLeftToBind) { // specified, and find the discovered argument types. if (this.returningName != null) { if (!this.argumentBindings.containsKey(this.returningName)) { - throw new IllegalStateException("Returning argument name '" - + this.returningName + "' was not bound in advice arguments"); + throw new IllegalStateException("Returning argument name '" + this.returningName + + "' was not bound in advice arguments"); } else { Integer index = this.argumentBindings.get(this.returningName); @@ -482,8 +482,8 @@ private void bindExplicitArguments(int numArgumentsLeftToBind) { } if (this.throwingName != null) { if (!this.argumentBindings.containsKey(this.throwingName)) { - throw new IllegalStateException("Throwing argument name '" - + this.throwingName + "' was not bound in advice arguments"); + throw new IllegalStateException("Throwing argument name '" + this.throwingName + + "' was not bound in advice arguments"); } else { Integer index = this.argumentBindings.get(this.throwingName); @@ -581,10 +581,9 @@ else if (this.joinPointStaticPartArgumentIndex != -1) { } if (numBound != this.adviceInvocationArgumentCount) { - throw new IllegalStateException("Required to bind " + this.adviceInvocationArgumentCount - + " arguments, but only bound " + numBound + " (JoinPointMatch " + - (jpMatch == null ? "was NOT" : "WAS") + - " bound in invocation)"); + throw new IllegalStateException("Required to bind " + this.adviceInvocationArgumentCount + + " arguments, but only bound " + numBound + " (JoinPointMatch " + + (jpMatch == null ? "was NOT" : "WAS") + " bound in invocation)"); } return adviceInvocationArgs; diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterAdvice.java index c6669af251..a1900b8998 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ public AspectJAfterAdvice( super(aspectJBeforeAdviceMethod, pointcut, aif); } + @Override public Object invoke(MethodInvocation mi) throws Throwable { try { diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterReturningAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterReturningAdvice.java index 59d5ae6e09..3a852bdb4f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterReturningAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterReturningAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,6 +40,7 @@ public AspectJAfterReturningAdvice( super(aspectJBeforeAdviceMethod, pointcut, aif); } + @Override public boolean isBeforeAdvice() { return false; @@ -62,6 +63,7 @@ public void afterReturning(Object returnValue, Method method, Object[] args, Obj } } + /** * Following AspectJ semantics, if a returning clause was specified, then the * advice is only invoked if the returned value is an instance of the given @@ -96,7 +98,7 @@ private boolean matchesReturnValue(Class type, Method method, Object returnVa else if (Object.class == type && void.class == method.getReturnType()) { return true; } - else{ + else { return ClassUtils.isAssignable(type, method.getReturnType()); } } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterThrowingAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterThrowingAdvice.java index 486b261275..be74c7e402 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterThrowingAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterThrowingAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ public AspectJAfterThrowingAdvice( super(aspectJBeforeAdviceMethod, pointcut, aif); } + @Override public boolean isBeforeAdvice() { return false; @@ -57,11 +58,11 @@ public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } - catch (Throwable t) { - if (shouldInvokeOnThrowing(t)) { - invokeAdviceMethod(getJoinPointMatch(), null, t); + catch (Throwable ex) { + if (shouldInvokeOnThrowing(ex)) { + invokeAdviceMethod(getJoinPointMatch(), null, ex); } - throw t; + throw ex; } } @@ -69,8 +70,8 @@ public Object invoke(MethodInvocation mi) throws Throwable { * In AspectJ semantics, after throwing advice that specifies a throwing clause * is only invoked if the thrown exception is a subtype of the given throwing type. */ - private boolean shouldInvokeOnThrowing(Throwable t) { - return getDiscoveredThrowingType().isAssignableFrom(t.getClass()); + private boolean shouldInvokeOnThrowing(Throwable ex) { + return getDiscoveredThrowingType().isAssignableFrom(ex.getClass()); } } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAroundAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAroundAdvice.java index b9c546342b..3efefd276c 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAroundAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAroundAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author 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,6 +41,7 @@ public AspectJAroundAdvice( super(aspectJAroundAdviceMethod, pointcut, aif); } + @Override public boolean isBeforeAdvice() { return false; @@ -56,7 +57,6 @@ protected boolean supportsProceedingJoinPoint() { return true; } - @Override public Object invoke(MethodInvocation mi) throws Throwable { if (!(mi instanceof ProxyMethodInvocation)) { 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 60427610a6..8d8f6a9e86 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author 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 org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.aspectj.weaver.BCException; import org.aspectj.weaver.patterns.NamePattern; import org.aspectj.weaver.reflect.ReflectionWorld.ReflectionWorldException; import org.aspectj.weaver.reflect.ShadowMatchImpl; @@ -258,7 +257,7 @@ public boolean matches(Class targetClass) { } } } - catch (BCException ex) { + catch (Throwable ex) { logger.debug("PointcutExpression matching rejected target class", ex); } return false; @@ -305,7 +304,7 @@ public boolean isRuntime() { } @Override - public boolean matches(Method method, Class targetClass, Object[] args) { + public boolean matches(Method method, Class targetClass, Object... args) { checkReadyToMatch(); ShadowMatch shadowMatch = getShadowMatch(AopUtils.getMostSpecificMethod(method, targetClass), method); ShadowMatch originalShadowMatch = getShadowMatch(method, method); @@ -326,7 +325,6 @@ public boolean matches(Method method, Class targetClass, Object[] args) { } catch (IllegalStateException ex) { // No current invocation... - // TODO: Should we really proceed here? if (logger.isDebugEnabled()) { logger.debug("Could not access current invocation - matching with limited context: " + ex); } @@ -413,39 +411,46 @@ private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) { shadowMatch = this.shadowMatchCache.get(targetMethod); if (shadowMatch == null) { try { - shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch); - } - catch (ReflectionWorldException ex) { - // Failed to introspect target method, probably because it has been loaded - // in a special ClassLoader. Let's try the declaring ClassLoader instead... - try { - fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass()); - if (fallbackExpression != null) { - shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch); - } - } - catch (ReflectionWorldException ex2) { - fallbackExpression = null; - } - } - if (shadowMatch == null && targetMethod != originalMethod) { - methodToMatch = originalMethod; try { shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch); } - catch (ReflectionWorldException ex3) { - // Could neither introspect the target class nor the proxy class -> - // let's try the original method's declaring class before we give up... + catch (ReflectionWorldException ex) { + // Failed to introspect target method, probably because it has been loaded + // in a special ClassLoader. Let's try the declaring ClassLoader instead... try { fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass()); if (fallbackExpression != null) { shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch); } } - catch (ReflectionWorldException ex4) { + catch (ReflectionWorldException ex2) { fallbackExpression = null; } } + if (shadowMatch == null && targetMethod != originalMethod) { + methodToMatch = originalMethod; + try { + shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch); + } + catch (ReflectionWorldException ex3) { + // Could neither introspect the target class nor the proxy class -> + // let's try the original method's declaring class before we give up... + try { + fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass()); + if (fallbackExpression != null) { + shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch); + } + } + catch (ReflectionWorldException ex4) { + fallbackExpression = null; + } + } + } + } + catch (Throwable ex) { + // Possibly AspectJ 1.8.10 encountering an invalid signature + logger.debug("PointcutExpression matching rejected target method", ex); + fallbackExpression = null; } if (shadowMatch == null) { shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null); diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJMethodBeforeAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJMethodBeforeAdvice.java index 6f7ce26b39..65a5dce1be 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJMethodBeforeAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJMethodBeforeAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author 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 @@ public AspectJMethodBeforeAdvice( super(aspectJBeforeAdviceMethod, pointcut, aif); } + @Override public void before(Method method, Object[] args, Object target) throws Throwable { invokeAdviceMethod(getJoinPointMatch(), null, null); 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 3fad461004..98c74f7ad0 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-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,34 +61,6 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac private static final String AJC_MAGIC = "ajc$"; - /** - * Find and return the first AspectJ annotation on the given method - * (there should only be one anyway...) - */ - @SuppressWarnings("unchecked") - 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); - if (foundAnnotation != null) { - return foundAnnotation; - } - } - return null; - } - - private static AspectJAnnotation findAnnotation(Method method, Class toLookFor) { - A result = AnnotationUtils.findAnnotation(method, toLookFor); - if (result != null) { - return new AspectJAnnotation(result); - } - else { - return null; - } - } - - /** Logger available to subclasses */ protected final Log logger = LogFactory.getLog(getClass()); @@ -181,6 +153,7 @@ private Class[] extractPointcutParameterTypes(String[] argNames, Method advic throw new IllegalStateException("Expecting at least " + argNames.length + " arguments in the advice declaration, but only found " + paramTypes.length); } + // Make the simplifying assumption for now that all of the JoinPoint based arguments // come first in the advice declaration. int typeOffset = paramTypes.length - argNames.length; @@ -191,7 +164,36 @@ private Class[] extractPointcutParameterTypes(String[] argNames, Method advic } + /** + * Find and return the first AspectJ annotation on the given method + * (there should only be one anyway...) + */ + @SuppressWarnings("unchecked") + 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); + if (foundAnnotation != null) { + return foundAnnotation; + } + } + return null; + } + + private static AspectJAnnotation findAnnotation(Method method, Class toLookFor) { + A result = AnnotationUtils.findAnnotation(method, toLookFor); + if (result != null) { + return new AspectJAnnotation(result); + } + else { + return null; + } + } + + protected enum AspectJAnnotationType { + AtPointcut, AtBefore, AtAfter, diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJAdvisorFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJAdvisorFactory.java index 36bf115105..7eba0606d5 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJAdvisorFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJAdvisorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author 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,31 +63,31 @@ public interface AspectJAdvisorFactory { /** * Build Spring AOP Advisors for all annotated At-AspectJ methods * on the specified aspect instance. - * @param aif the aspect instance factory (not the aspect instance itself - * in order to avoid eager instantiation) + * @param aspectInstanceFactory the aspect instance factory + * (not the aspect instance itself in order to avoid eager instantiation) * @return a list of advisors for this class */ - List getAdvisors(MetadataAwareAspectInstanceFactory aif); + List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory); /** * Build a Spring AOP Advisor for the given AspectJ advice method. * @param candidateAdviceMethod the candidate advice method - * @param aif the aspect instance factory - * @param declarationOrderInAspect the declaration order within the aspect + * @param aspectInstanceFactory the aspect instance factory + * @param declarationOrder the declaration order within the aspect * @param aspectName the name of the aspect * @return {@code null} if the method is not an AspectJ advice method * or if it is a pointcut that will be used by other advice but will not * create a Spring advice in its own right */ - Advisor getAdvisor(Method candidateAdviceMethod, - MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName); + Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, + int declarationOrder, String aspectName); /** * Build a Spring AOP Advice for the given AspectJ advice method. * @param candidateAdviceMethod the candidate advice method - * @param pointcut the corresponding AspectJ expression pointcut - * @param aif the aspect instance factory - * @param declarationOrderInAspect the declaration order within the aspect + * @param expressionPointcut the AspectJ expression pointcut + * @param aspectInstanceFactory the aspect instance factory + * @param declarationOrder the declaration order within the aspect * @param aspectName the name of the aspect * @return {@code null} if the method is not an AspectJ advice method * or if it is a pointcut that will be used by other advice but will not @@ -98,7 +98,7 @@ Advisor getAdvisor(Method candidateAdviceMethod, * @see org.springframework.aop.aspectj.AspectJAfterReturningAdvice * @see org.springframework.aop.aspectj.AspectJAfterThrowingAdvice */ - Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut pointcut, - MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName); + Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, + MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName); } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectMetadata.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectMetadata.java index a70010e1f9..d20e5222a0 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectMetadata.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectMetadata.java @@ -42,6 +42,13 @@ */ public class AspectMetadata { + /** + * The name of this aspect as defined to Spring (the bean name) - + * allows us to determine if two pieces of advice come from the + * same aspect and hence their relative precedence. + */ + private final String aspectName; + /** * AspectJ reflection information (AspectJ 5 / Java 5 specific). */ @@ -54,13 +61,6 @@ public class AspectMetadata { */ private final Pointcut perClausePointcut; - /** - * The name of this aspect as defined to Spring (the bean name) - - * allows us to determine if two pieces of advice come from the - * same aspect and hence their relative precedence. - */ - private String aspectName; - /** * Create a new AspectMetadata instance for the given aspect class. @@ -83,10 +83,10 @@ public AspectMetadata(Class aspectClass, String aspectName) { if (ajType == null) { throw new IllegalArgumentException("Class '" + aspectClass.getName() + "' is not an @AspectJ aspect"); } - this.ajType = ajType; - if (this.ajType.getDeclarePrecedence().length > 0) { + if (ajType.getDeclarePrecedence().length > 0) { throw new IllegalArgumentException("DeclarePrecendence not presently supported in Spring AOP"); } + this.ajType = ajType; switch (this.ajType.getPerClause().getKind()) { case SINGLETON : 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 4ac2b0282c..eb78d0bfbf 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-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.Ordered; import org.springframework.core.annotation.OrderUtils; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** @@ -66,6 +67,8 @@ public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name) { * @param type the type that should be introspected by AspectJ */ public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name, Class type) { + Assert.notNull(beanFactory, "BeanFactory must not be null"); + Assert.notNull(name, "Bean name must not be null"); this.beanFactory = beanFactory; this.name = name; this.aspectMetadata = new AspectMetadata(type, name); @@ -79,12 +82,9 @@ public Object getAspectInstance() { @Override public ClassLoader getAspectClassLoader() { - if (this.beanFactory instanceof ConfigurableBeanFactory) { - return ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader(); - } - else { - return ClassUtils.getDefaultClassLoader(); - } + return (this.beanFactory instanceof ConfigurableBeanFactory ? + ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() : + ClassUtils.getDefaultClassLoader()); } @Override @@ -92,6 +92,22 @@ public AspectMetadata getAspectMetadata() { return this.aspectMetadata; } + public Object getAspectCreationMutex() { + if (this.beanFactory != null) { + if (this.beanFactory.isSingleton(name)) { + // Rely on singleton semantics provided by the factory -> no local lock. + return null; + } + else if (this.beanFactory instanceof ConfigurableBeanFactory) { + // No singleton guarantees from the factory -> let's lock locally but + // reuse the factory's singleton lock, just in case a lazy dependency + // of our advice bean happens to trigger the singleton lock implicitly... + return ((ConfigurableBeanFactory) this.beanFactory).getSingletonMutex(); + } + } + return this; + } + /** * Determine the order for this factory's target aspect, either * an instance-specific order expressed through implementing the 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 200936f140..750a1eb265 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-2012 the original author or authors. + * Copyright 2002-2015 the original author 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,53 +42,55 @@ class InstantiationModelAwarePointcutAdvisorImpl private final AspectJExpressionPointcut declaredPointcut; - private Pointcut pointcut; + private final Method aspectJAdviceMethod; - private final MetadataAwareAspectInstanceFactory aspectInstanceFactory; + private final AspectJAdvisorFactory aspectJAdvisorFactory; - private final Method method; + private final MetadataAwareAspectInstanceFactory aspectInstanceFactory; - private final boolean lazy; + private final int declarationOrder; - private final AspectJAdvisorFactory atAspectJAdvisorFactory; + private final String aspectName; - private Advice instantiatedAdvice; + private final Pointcut pointcut; - private int declarationOrder; + private final boolean lazy; - private String aspectName; + private Advice instantiatedAdvice; private Boolean isBeforeAdvice; private Boolean isAfterAdvice; - public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp, - MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName) { + public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, + Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, + MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { - this.declaredPointcut = ajexp; - this.method = method; - this.atAspectJAdvisorFactory = af; - this.aspectInstanceFactory = aif; - this.declarationOrder = declarationOrderInAspect; + this.declaredPointcut = declaredPointcut; + this.aspectJAdviceMethod = aspectJAdviceMethod; + this.aspectJAdvisorFactory = aspectJAdvisorFactory; + this.aspectInstanceFactory = aspectInstanceFactory; + this.declarationOrder = declarationOrder; this.aspectName = aspectName; - if (aif.getAspectMetadata().isLazilyInstantiated()) { + if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { // Static part of the pointcut is a lazy type. - Pointcut preInstantiationPointcut = - Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut); + Pointcut preInstantiationPointcut = Pointcuts.union( + aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut); // Make it dynamic: must mutate from pre-instantiation to post-instantiation state. // If it's not a dynamic pointcut, it may be optimized out // by the Spring AOP infrastructure after the first evaluation. - this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aif); + this.pointcut = new PerTargetInstantiationModelPointcut( + this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory); this.lazy = true; } else { // A singleton aspect. - this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); - this.pointcut = declaredPointcut; + this.pointcut = this.declaredPointcut; this.lazy = false; + this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); } } @@ -142,8 +144,8 @@ public synchronized boolean isAdviceInstantiated() { private Advice instantiateAdvice(AspectJExpressionPointcut pcut) { - return this.atAspectJAdvisorFactory.getAdvice( - this.method, pcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName); + return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut, + this.aspectInstanceFactory, this.declarationOrder, this.aspectName); } public MetadataAwareAspectInstanceFactory getAspectInstanceFactory() { @@ -191,7 +193,7 @@ public boolean isAfterAdvice() { */ private void determineAdviceType() { AspectJAnnotation aspectJAnnotation = - AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(this.method); + AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(this.aspectJAdviceMethod); if (aspectJAnnotation == null) { this.isBeforeAdvice = false; this.isAfterAdvice = false; @@ -220,7 +222,7 @@ private void determineAdviceType() { @Override public String toString() { return "InstantiationModelAwarePointcutAdvisor: expression [" + getDeclaredPointcut().getExpression() + - "]; advice method [" + this.method + "]; perClauseKind=" + + "]; advice method [" + this.aspectJAdviceMethod + "]; perClauseKind=" + this.aspectInstanceFactory.getAspectMetadata().getAjType().getPerClause().getKind(); } @@ -256,7 +258,7 @@ public boolean matches(Method method, Class targetClass) { } @Override - public boolean matches(Method method, 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/LazySingletonAspectInstanceFactoryDecorator.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java index 36bb4eeb42..0f793cafef 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,11 +43,20 @@ public LazySingletonAspectInstanceFactoryDecorator(MetadataAwareAspectInstanceFa @Override - public synchronized Object getAspectInstance() { + public Object getAspectInstance() { if (this.materialized == null) { - synchronized (this) { - if (this.materialized == null) { - this.materialized = this.maaif.getAspectInstance(); + Object mutex = this; + if (this.maaif instanceof BeanFactoryAspectInstanceFactory) { + mutex = ((BeanFactoryAspectInstanceFactory) this.maaif).getAspectCreationMutex(); + } + if (mutex == null) { + this.materialized = this.maaif.getAspectInstance(); + } + else { + synchronized (mutex) { + if (this.materialized == null) { + this.materialized = this.maaif.getAspectInstance(); + } } } } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/PrototypeAspectInstanceFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/PrototypeAspectInstanceFactory.java index 5d6fc9079b..fc2978a9a8 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/PrototypeAspectInstanceFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/PrototypeAspectInstanceFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2015 the original author 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,8 +19,8 @@ import org.springframework.beans.factory.BeanFactory; /** - * AspectInstanceFactory backed by a BeanFactory-provided prototype, - * enforcing prototype semantics. + * {@link org.springframework.aop.aspectj.AspectInstanceFactory} backed by a + * {@link BeanFactory}-provided prototype, enforcing prototype semantics. * *

Note that this may instantiate multiple times, which probably won't give the * semantics you expect. Use a {@link LazySingletonAspectInstanceFactoryDecorator} 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 bf8cab2b08..1a35493001 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-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -77,8 +77,9 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto new Converter() { @Override public Annotation convert(Method method) { - AspectJAnnotation annotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); - return annotation == null ? null : annotation.getAnnotation(); + AspectJAnnotation annotation = + AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); + return (annotation != null ? annotation.getAnnotation() : null); } })); comparator.addComparator(new ConvertingComparator( @@ -93,17 +94,17 @@ public String convert(Method method) { @Override - public List getAdvisors(MetadataAwareAspectInstanceFactory maaif) { - final Class aspectClass = maaif.getAspectMetadata().getAspectClass(); - final String aspectName = maaif.getAspectMetadata().getAspectName(); + public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { + Class aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); + String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); validate(aspectClass); // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator // so that it will only instantiate once. - final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = - new LazySingletonAspectInstanceFactoryDecorator(maaif); + MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = + new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); - final List advisors = new LinkedList(); + List advisors = new LinkedList(); for (Method method : getAdvisorMethods(aspectClass)) { Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); if (advisor != null) { @@ -169,18 +170,19 @@ private Advisor getDeclareParentsAdvisor(Field introductionField) { @Override - public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif, + public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { - validate(aif.getAspectMetadata().getAspectClass()); + validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); - AspectJExpressionPointcut ajexp = - getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass()); - if (ajexp == null) { + AspectJExpressionPointcut expressionPointcut = getPointcut( + candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); + if (expressionPointcut == null) { return null; } - return new InstantiationModelAwarePointcutAdvisorImpl( - this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName); + + return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, + this, aspectInstanceFactory, declarationOrderInAspect, aspectName); } private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class candidateAspectClass) { @@ -189,6 +191,7 @@ private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Clas if (aspectJAnnotation == null) { return null; } + AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]); ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); @@ -197,10 +200,10 @@ private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Clas @Override - public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp, - MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) { + public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, + MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { - Class candidateAspectClass = aif.getAspectMetadata().getAspectClass(); + Class candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); validate(candidateAspectClass); AspectJAnnotation aspectJAnnotation = @@ -225,27 +228,32 @@ public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut switch (aspectJAnnotation.getAnnotationType()) { case AtBefore: - springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif); + springAdvice = new AspectJMethodBeforeAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfter: - springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif); + springAdvice = new AspectJAfterAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfterReturning: - springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif); + springAdvice = new AspectJAfterReturningAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterReturningAnnotation.returning())) { springAdvice.setReturningName(afterReturningAnnotation.returning()); } break; case AtAfterThrowing: - springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif); + springAdvice = new AspectJAfterThrowingAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); } break; case AtAround: - springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif); + springAdvice = new AspectJAroundAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtPointcut: if (logger.isDebugEnabled()) { @@ -254,12 +262,12 @@ public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut return null; default: throw new UnsupportedOperationException( - "Unsupported advice type on method " + candidateAdviceMethod); + "Unsupported advice type on method: " + candidateAdviceMethod); } // Now to configure the advice... springAdvice.setAspectName(aspectName); - springAdvice.setDeclarationOrder(declarationOrderInAspect); + springAdvice.setDeclarationOrder(declarationOrder); String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); if (argNames != null) { springAdvice.setArgumentNamesFromStringArray(argNames); @@ -268,6 +276,7 @@ public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut return springAdvice; } + /** * Synthetic advisor that instantiates the aspect. * Triggered by per-clause pointcut on non-singleton aspect. 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 ed95bd96de..9b3b307467 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -113,7 +113,7 @@ public AdvisedSupport() { * Create a AdvisedSupport instance with the given parameters. * @param interfaces the proxied interfaces */ - public AdvisedSupport(Class[] interfaces) { + public AdvisedSupport(Class... interfaces) { this(); setInterfaces(interfaces); } diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java index db1e19a359..854f9122f6 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author 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,9 +22,9 @@ import org.springframework.util.ClassUtils; /** - * Factory for AOP proxies for programmatic use, rather than via a bean - * factory. This class provides a simple way of obtaining and configuring - * AOP proxies in code. + * Factory for AOP proxies for programmatic use, rather than via declarative + * setup in a bean factory. This class provides a simple way of obtaining + * and configuring AOP proxy instances in custom user code. * * @author Rod Johnson * @author Juergen Hoeller 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 134bc2f3e0..b1cdaa0f20 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-2015 the original author or authors. + * Copyright 2002-2016 the original author 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,9 +31,12 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.core.task.AsyncListenableTaskExecutor; import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.core.task.TaskExecutor; import org.springframework.core.task.support.TaskExecutorAdapter; import org.springframework.lang.UsesJava8; import org.springframework.util.ClassUtils; @@ -58,6 +61,15 @@ */ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { + /** + * The default name of the {@link TaskExecutor} bean to pick up: "taskExecutor". + *

Note that the initial lookup happens by type; this is just the fallback + * in case of multiple executor beans found in the context. + * @since 4.2.6 + */ + public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME = "taskExecutor"; + + // Java 8's CompletableFuture type present? private static final boolean completableFuturePresent = ClassUtils.isPresent( "java.util.concurrent.CompletableFuture", AsyncExecutionInterceptor.class.getClassLoader()); @@ -67,7 +79,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { private final Map executors = new ConcurrentHashMap(16); - private Executor defaultExecutor; + private volatile Executor defaultExecutor; private AsyncUncaughtExceptionHandler exceptionHandler; @@ -75,34 +87,39 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { /** - * Create a new {@link AsyncExecutionAspectSupport}, using the provided default - * executor unless individual async methods indicate via qualifier that a more - * specific executor should be used. - * @param defaultExecutor the executor to use when executing asynchronous methods - * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use + * Create a new instance with a default {@link AsyncUncaughtExceptionHandler}. + * @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor} + * or {@link java.util.concurrent.ExecutorService}) to delegate to, unless a more specific + * executor has been requested via a qualifier on the async method, in which case the + * executor will be looked up at invocation time against the enclosing bean factory */ - public AsyncExecutionAspectSupport(Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) { - this.defaultExecutor = defaultExecutor; - this.exceptionHandler = exceptionHandler; + public AsyncExecutionAspectSupport(Executor defaultExecutor) { + this(defaultExecutor, new SimpleAsyncUncaughtExceptionHandler()); } /** - * Create a new instance with a default {@link AsyncUncaughtExceptionHandler}. + * Create a new {@link AsyncExecutionAspectSupport} with the given exception handler. + * @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor} + * or {@link java.util.concurrent.ExecutorService}) to delegate to, unless a more specific + * executor has been requested via a qualifier on the async method, in which case the + * executor will be looked up at invocation time against the enclosing bean factory + * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use */ - public AsyncExecutionAspectSupport(Executor defaultExecutor) { - this(defaultExecutor, new SimpleAsyncUncaughtExceptionHandler()); + public AsyncExecutionAspectSupport(Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) { + this.defaultExecutor = defaultExecutor; + this.exceptionHandler = exceptionHandler; } /** * Supply the executor to be used when executing async methods. - * @param defaultExecutor the {@code Executor} (typically a Spring {@code - * AsyncTaskExecutor} or {@link java.util.concurrent.ExecutorService}) to delegate to - * unless a more specific executor has been requested via a qualifier on the async - * method, in which case the executor will be looked up at invocation time against the - * enclosing bean factory. - * @see #getExecutorQualifier + * @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor} + * or {@link java.util.concurrent.ExecutorService}) to delegate to, unless a more specific + * executor has been requested via a qualifier on the async method, in which case the + * executor will be looked up at invocation time against the enclosing bean factory + * @see #getExecutorQualifier(Method) * @see #setBeanFactory(BeanFactory) + * @see #getDefaultExecutor(BeanFactory) */ public void setExecutor(Executor defaultExecutor) { this.defaultExecutor = defaultExecutor; @@ -117,7 +134,10 @@ public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) } /** - * Set the {@link BeanFactory} to be used when looking up executors by qualifier. + * Set the {@link BeanFactory} to be used when looking up executors by qualifier + * or when relying on the default executor lookup algorithm. + * @see #findQualifiedExecutor(BeanFactory, String) + * @see #getDefaultExecutor(BeanFactory) */ @Override public void setBeanFactory(BeanFactory beanFactory) { @@ -128,26 +148,32 @@ public void setBeanFactory(BeanFactory beanFactory) { /** * Determine the specific executor to use when executing the given method. * Should preferably return an {@link AsyncListenableTaskExecutor} implementation. - * @return the executor to use (or {@code null}, but just if no default executor has been set) + * @return the executor to use (or {@code null}, but just if no default executor is available) */ protected AsyncTaskExecutor determineAsyncExecutor(Method method) { AsyncTaskExecutor executor = this.executors.get(method); if (executor == null) { - Executor executorToUse = this.defaultExecutor; + Executor targetExecutor; String qualifier = getExecutorQualifier(method); if (StringUtils.hasLength(qualifier)) { - if (this.beanFactory == null) { - throw new IllegalStateException("BeanFactory must be set on " + getClass().getSimpleName() + - " to access qualified executor '" + qualifier + "'"); + 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; + } } - executorToUse = BeanFactoryAnnotationUtils.qualifiedBeanOfType( - this.beanFactory, Executor.class, qualifier); } - else if (executorToUse == null) { + if (targetExecutor == null) { return null; } - executor = (executorToUse instanceof AsyncListenableTaskExecutor ? - (AsyncListenableTaskExecutor) executorToUse : new TaskExecutorAdapter(executorToUse)); + executor = (targetExecutor instanceof AsyncListenableTaskExecutor ? + (AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor)); this.executors.put(method, executor); } return executor; @@ -160,11 +186,69 @@ else if (executorToUse == null) { * been specified and that the {@linkplain #setExecutor(Executor) default executor} * should be used. * @param method the method to inspect for executor qualifier metadata - * @return the qualifier if specified, otherwise empty string or {@code null} + * @return the qualifier if specified, otherwise empty String or {@code null} * @see #determineAsyncExecutor(Method) + * @see #findQualifiedExecutor(BeanFactory, String) */ protected abstract String getExecutorQualifier(Method method); + /** + * Retrieve a target executor for the given qualifier. + * @param qualifier the qualifier to resolve + * @return the target executor, or {@code null} if none available + * @since 4.2.6 + * @see #getExecutorQualifier(Method) + */ + protected Executor findQualifiedExecutor(BeanFactory beanFactory, String qualifier) { + if (beanFactory == null) { + throw new IllegalStateException("BeanFactory must be set on " + getClass().getSimpleName() + + " to access qualified executor '" + qualifier + "'"); + } + return BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, Executor.class, qualifier); + } + + /** + * Retrieve or build a default executor for this advice instance. + * An executor returned from here will be cached for further use. + *

The default implementation searches for a unique {@link TaskExecutor} bean + * in the context, or for an {@link Executor} bean named "taskExecutor" otherwise. + * If neither of the two is resolvable, this implementation will return {@code null}. + * @param beanFactory the BeanFactory to use for a default executor lookup + * @return the default executor, or {@code null} if none available + * @since 4.2.6 + * @see #findQualifiedExecutor(BeanFactory, String) + * @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME + */ + protected Executor getDefaultExecutor(BeanFactory beanFactory) { + if (beanFactory != null) { + try { + // Search for TaskExecutor bean... not plain Executor since that would + // match with ScheduledExecutorService as well, which is unusable for + // our purposes here. TaskExecutor is more clearly designed for it. + return beanFactory.getBean(TaskExecutor.class); + } + catch (NoUniqueBeanDefinitionException ex) { + try { + return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class); + } + catch (NoSuchBeanDefinitionException ex2) { + if (logger.isInfoEnabled()) { + logger.info("More than one TaskExecutor bean found within the context, and none is named " + + "'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly " + + "as an alias) in order to use it for async processing."); + } + } + } + catch (NoSuchBeanDefinitionException ex) { + logger.debug("Could not find default TaskExecutor bean", ex); + // Giving up -> either using local default executor or none at all... + logger.info("No TaskExecutor bean found for async processing"); + } + } + return null; + } + + /** * Delegate for actually executing the given task with the chosen executor. * @param task the task to execute 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 8aa0c020ac..a758fd2576 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,9 +26,11 @@ import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.support.AopUtils; +import org.springframework.beans.factory.BeanFactory; import org.springframework.core.BridgeMethodResolver; import org.springframework.core.Ordered; import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.util.ClassUtils; /** @@ -65,22 +67,27 @@ */ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered { + /** + * Create a new instance with a default {@link AsyncUncaughtExceptionHandler}. + * @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor} + * or {@link java.util.concurrent.ExecutorService}) to delegate to; + * as of 4.2.6, a local executor for this interceptor will be built otherwise + */ + public AsyncExecutionInterceptor(Executor defaultExecutor) { + super(defaultExecutor); + } + /** * Create a new {@code AsyncExecutionInterceptor}. * @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor} - * or {@link java.util.concurrent.ExecutorService}) to delegate to + * or {@link java.util.concurrent.ExecutorService}) to delegate to; + * as of 4.2.6, a local executor for this interceptor will be built otherwise * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use */ public AsyncExecutionInterceptor(Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) { super(defaultExecutor, exceptionHandler); } - /** - * Create a new instance with a default {@link AsyncUncaughtExceptionHandler}. - */ - public AsyncExecutionInterceptor(Executor defaultExecutor) { - super(defaultExecutor); - } /** * Intercept the given method invocation, submit the actual calling of the method to @@ -136,6 +143,20 @@ protected String getExecutorQualifier(Method method) { return null; } + /** + * This implementation searches for a unique {@link org.springframework.core.task.TaskExecutor} + * bean in the context, or for an {@link Executor} bean named "taskExecutor" otherwise. + * If neither of the two is resolvable (e.g. if no {@code BeanFactory} was configured at all), + * this implementation falls back to a newly created {@link SimpleAsyncTaskExecutor} instance + * for local use if no default could be found. + * @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME + */ + @Override + protected Executor getDefaultExecutor(BeanFactory beanFactory) { + Executor defaultExecutor = super.getDefaultExecutor(beanFactory); + return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor()); + } + @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; diff --git a/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java b/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java index e46ee28c26..5401bb96a7 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.util.Assert; /** @@ -45,7 +46,7 @@ public abstract class AbstractBeanFactoryPointcutAdvisor extends AbstractPointcu private BeanFactory beanFactory; - private transient Advice advice; + private transient volatile Advice advice; private transient volatile Object adviceMonitor = new Object(); @@ -72,8 +73,23 @@ public String getAdviceBeanName() { @Override public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; + resetAdviceMonitor(); } + private void resetAdviceMonitor() { + if (this.beanFactory instanceof ConfigurableBeanFactory) { + this.adviceMonitor = ((ConfigurableBeanFactory) this.beanFactory).getSingletonMutex(); + } + else { + this.adviceMonitor = new Object(); + } + } + + /** + * Specify a particular instance of the target advice directly, + * avoiding lazy resolution in {@link #getAdvice()}. + * @since 3.1 + */ public void setAdvice(Advice advice) { synchronized (this.adviceMonitor) { this.advice = advice; @@ -82,18 +98,42 @@ public void setAdvice(Advice advice) { @Override public Advice getAdvice() { - synchronized (this.adviceMonitor) { - if (this.advice == null && this.adviceBeanName != null) { - Assert.state(this.beanFactory != null, "BeanFactory must be set to resolve 'adviceBeanName'"); - this.advice = this.beanFactory.getBean(this.adviceBeanName, Advice.class); + Advice advice = this.advice; + if (advice != null || this.adviceBeanName == null) { + return advice; + } + + Assert.state(this.beanFactory != null, "BeanFactory must be set to resolve 'adviceBeanName'"); + if (this.beanFactory.isSingleton(this.adviceBeanName)) { + // Rely on singleton semantics provided by the factory. + advice = this.beanFactory.getBean(this.adviceBeanName, Advice.class); + this.advice = advice; + return advice; + } + else { + // No singleton guarantees from the factory -> let's lock locally but + // reuse the factory's singleton lock, just in case a lazy dependency + // of our advice bean happens to trigger the singleton lock implicitly... + synchronized (this.adviceMonitor) { + if (this.advice == null) { + this.advice = this.beanFactory.getBean(this.adviceBeanName, Advice.class); + } + return this.advice; } - return this.advice; } } @Override public String toString() { - return getClass().getName() + ": advice bean '" + getAdviceBeanName() + "'"; + StringBuilder sb = new StringBuilder(getClass().getName()); + sb.append(": advice "); + if (this.adviceBeanName != null) { + sb.append("bean '").append(this.adviceBeanName).append("'"); + } + else { + sb.append(this.advice); + } + return sb.toString(); } @@ -106,7 +146,7 @@ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFound ois.defaultReadObject(); // Initialize transient fields. - this.adviceMonitor = new Object(); + resetAdviceMonitor(); } } 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 d41a36c250..1989a6dae4 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-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -91,17 +91,17 @@ public boolean isRuntime() { } @Override - public boolean matches(Method method, Class targetClass, Object[] args) { - ++this.evaluations; + public boolean matches(Method method, Class targetClass, Object... args) { + this.evaluations++; ControlFlow cflow = ControlFlowFactory.createControlFlow(); - return (this.methodName != null) ? cflow.under(this.clazz, this.methodName) : cflow.under(this.clazz); + return (this.methodName != null ? cflow.under(this.clazz, this.methodName) : cflow.under(this.clazz)); } /** * It's useful to know how many times we've fired, for optimization. */ public int getEvaluations() { - return evaluations; + return this.evaluations; } @@ -115,6 +115,7 @@ public MethodMatcher getMethodMatcher() { return this; } + @Override public boolean equals(Object other) { if (this == other) { 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 b71e2c2399..9a64d07431 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -138,7 +138,7 @@ public boolean isRuntime() { } @Override - public boolean matches(Method method, 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); } @@ -245,7 +245,7 @@ public boolean isRuntime() { } @Override - public boolean matches(Method method, 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. 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 df6e57bfa4..b17977ba48 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-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -71,7 +71,7 @@ public static Pointcut intersection(Pointcut pc1, Pointcut pc2) { * @param args arguments to the method * @return whether there's a runtime match */ - public static boolean matches(Pointcut pointcut, Method method, Class targetClass, Object[] args) { + public static boolean matches(Pointcut pointcut, Method method, Class targetClass, Object... args) { Assert.notNull(pointcut, "Pointcut must not be null"); if (pointcut == Pointcut.TRUE) { return true; @@ -98,9 +98,9 @@ private static class SetterPointcut extends StaticMethodMatcherPointcut implemen @Override public boolean matches(Method method, Class targetClass) { - return method.getName().startsWith("set") && - method.getParameterTypes().length == 1 && - method.getReturnType() == Void.TYPE; + return (method.getName().startsWith("set") && + method.getParameterTypes().length == 1 && + method.getReturnType() == Void.TYPE); } private Object readResolve() { @@ -119,8 +119,8 @@ private static class GetterPointcut extends StaticMethodMatcherPointcut implemen @Override public boolean matches(Method method, Class targetClass) { - return method.getName().startsWith("get") && - method.getParameterTypes().length == 0; + return (method.getName().startsWith("get") && + method.getParameterTypes().length == 0); } private Object readResolve() { 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 8bb1d74d73..0627248ee9 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-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,7 @@ public final boolean isRuntime() { } @Override - public final boolean matches(Method method, 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/target/dynamic/AbstractRefreshableTargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/dynamic/AbstractRefreshableTargetSource.java index c96c21e193..7bab7e0771 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-2012 the original author or authors. + * Copyright 2002-2016 the original author 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 */ - protected Log logger = LogFactory.getLog(getClass()); + protected final Log logger = LogFactory.getLog(getClass()); protected Object targetObject; diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJExpressionPointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJExpressionPointcutTests.java index 6419fbe774..28622ba8d2 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJExpressionPointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJExpressionPointcutTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,17 +53,15 @@ public final class AspectJExpressionPointcutTests { private Method setSomeNumber; - private Method isPostProcessed; - @Before public void setUp() throws NoSuchMethodException { - getAge = TestBean.class.getMethod("getAge", (Class[])null); - setAge = TestBean.class.getMethod("setAge", new Class[]{int.class}); - setSomeNumber = TestBean.class.getMethod("setSomeNumber", new Class[]{Number.class}); - isPostProcessed = TestBean.class.getMethod("isPostProcessed", (Class[]) null); + getAge = TestBean.class.getMethod("getAge"); + setAge = TestBean.class.getMethod("setAge", int.class); + setSomeNumber = TestBean.class.getMethod("setSomeNumber", Number.class); } + @Test public void testMatchExplicit() { String expression = "execution(int org.springframework.tests.sample.beans.TestBean.getAge())"; @@ -111,21 +109,9 @@ public void testTarget() throws SecurityException, NoSuchMethodException { testThisOrTarget("target"); } - public static class OtherIOther implements IOther { - - @Override - public void absquatulate() { - // Empty - } - - } - /** * This and target are equivalent. Really instanceof pointcuts. * @param which this or target - * @throws Exception - * @throws NoSuchMethodException - * @throws SecurityException */ private void testThisOrTarget(String which) throws SecurityException, NoSuchMethodException { String matchesTestBean = which + "(org.springframework.tests.sample.beans.TestBean)"; @@ -138,11 +124,8 @@ private void testThisOrTarget(String which) throws SecurityException, NoSuchMeth assertTrue(testBeanPc.matches(TestBean.class)); assertTrue(testBeanPc.matches(getAge, TestBean.class)); - assertTrue(iOtherPc.matches(OtherIOther.class.getMethod("absquatulate", (Class[])null), - OtherIOther.class)); - - assertFalse(testBeanPc.matches(OtherIOther.class.getMethod("absquatulate", (Class[])null), - OtherIOther.class)); + assertTrue(iOtherPc.matches(OtherIOther.class.getMethod("absquatulate"), OtherIOther.class)); + assertFalse(testBeanPc.matches(OtherIOther.class.getMethod("absquatulate"), OtherIOther.class)); } @Test @@ -171,8 +154,7 @@ private void testWithinPackage(boolean matchSubpackages) throws SecurityExceptio assertEquals(matchSubpackages, withinBeansPc.matches( DeepBean.class.getMethod("aMethod", String.class), DeepBean.class)); assertFalse(withinBeansPc.matches(String.class)); - assertFalse(withinBeansPc.matches(OtherIOther.class.getMethod("absquatulate", (Class[])null), - OtherIOther.class)); + assertFalse(withinBeansPc.matches(OtherIOther.class.getMethod("absquatulate"), OtherIOther.class)); } @Test @@ -183,7 +165,7 @@ public void testFriendlyErrorOnNoLocationClassMatching() { fail(); } catch (IllegalStateException ex) { - assertTrue(ex.getMessage().indexOf("expression") != -1); + assertTrue(ex.getMessage().contains("expression")); } } @@ -195,7 +177,7 @@ public void testFriendlyErrorOnNoLocation2ArgMatching() { fail(); } catch (IllegalStateException ex) { - assertTrue(ex.getMessage().indexOf("expression") != -1); + assertTrue(ex.getMessage().contains("expression")); } } @@ -207,7 +189,7 @@ public void testFriendlyErrorOnNoLocation3ArgMatching() { fail(); } catch (IllegalStateException ex) { - assertTrue(ex.getMessage().indexOf("expression") != -1); + assertTrue(ex.getMessage().contains("expression")); } } @@ -226,10 +208,10 @@ public void testMatchWithArgs() throws Exception { //assertDoesNotMatchStringClass(classFilter); assertTrue("Should match with setSomeNumber with Double input", - methodMatcher.matches(setSomeNumber, TestBean.class, new Object[]{new Double(12)})); + methodMatcher.matches(setSomeNumber, TestBean.class, new Double(12))); assertFalse("Should not match setSomeNumber with Integer input", - methodMatcher.matches(setSomeNumber, TestBean.class, new Object[]{new Integer(11)})); - assertFalse("Should not match getAge", methodMatcher.matches(getAge, TestBean.class, null)); + methodMatcher.matches(setSomeNumber, TestBean.class, new Integer(11))); + assertFalse("Should not match getAge", methodMatcher.matches(getAge, TestBean.class)); assertTrue("Should be a runtime match", methodMatcher.isRuntime()); } @@ -281,7 +263,6 @@ public void testInvalidExpression() { } catch (IllegalArgumentException ex) { assertTrue(true); - System.out.println(ex.getMessage()); } } @@ -326,16 +307,14 @@ public void testWithUnsupportedPointcutPrimitive() throws Exception { @Test public void testAndSubstitution() { Pointcut pc = getPointcut("execution(* *(..)) and args(String)"); - PointcutExpression expr = - ((AspectJExpressionPointcut) pc).getPointcutExpression(); + PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression(); assertEquals("execution(* *(..)) && args(String)",expr.getPointcutExpression()); } @Test public void testMultipleAndSubstitutions() { Pointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)"); - PointcutExpression expr = - ((AspectJExpressionPointcut) pc).getPointcutExpression(); + PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression(); assertEquals("execution(* *(..)) && args(String) && this(Object)",expr.getPointcutExpression()); } @@ -344,6 +323,15 @@ private Pointcut getPointcut(String expression) { pointcut.setExpression(expression); return pointcut; } + + + public static class OtherIOther implements IOther { + + @Override + public void absquatulate() { + // Empty + } + } } diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/TigerAspectJExpressionPointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/TigerAspectJExpressionPointcutTests.java index f14bee5dfc..ddb7c1afe2 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/TigerAspectJExpressionPointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/TigerAspectJExpressionPointcutTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author 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 @@ * @author Rod Johnson * @author Chris Beams */ -public final class TigerAspectJExpressionPointcutTests { +public class TigerAspectJExpressionPointcutTests { // TODO factor into static in AspectJExpressionPointcut private Method getAge; @@ -46,7 +46,7 @@ public final class TigerAspectJExpressionPointcutTests { @Before public void setUp() throws NoSuchMethodException { - getAge = TestBean.class.getMethod("getAge", (Class[]) null); + getAge = TestBean.class.getMethod("getAge"); // Assumes no overloading for (Method m : HasGeneric.class.getMethods()) { methodsOnHasGeneric.put(m.getName(), m); @@ -54,18 +54,6 @@ public void setUp() throws NoSuchMethodException { } - public static class HasGeneric { - - public void setFriends(List friends) { - } - public void setEnemies(List enemies) { - } - public void setPartners(List partners) { - } - public void setPhoneNumbers(List numbers) { - } - } - @Test public void testMatchGenericArgument() { String expression = "execution(* set*(java.util.List) )"; @@ -132,15 +120,12 @@ public void testMatchAnnotationOnClassWithoutBinding() throws SecurityException, public void testMatchAnnotationOnClassWithSubpackageWildcard() throws SecurityException, NoSuchMethodException { String expression = "within(@(test.annotation..*) *)"; AspectJExpressionPointcut springAnnotatedPc = testMatchAnnotationOnClass(expression); - assertFalse(springAnnotatedPc.matches(TestBean.class.getMethod("setName", String.class), - TestBean.class)); - assertTrue(springAnnotatedPc.matches(SpringAnnotated.class.getMethod("foo", (Class[]) null), - SpringAnnotated.class)); + assertFalse(springAnnotatedPc.matches(TestBean.class.getMethod("setName", String.class), TestBean.class)); + assertTrue(springAnnotatedPc.matches(SpringAnnotated.class.getMethod("foo"), SpringAnnotated.class)); expression = "within(@(test.annotation.transaction..*) *)"; AspectJExpressionPointcut springTxAnnotatedPc = testMatchAnnotationOnClass(expression); - assertFalse(springTxAnnotatedPc.matches(SpringAnnotated.class.getMethod("foo", (Class[]) null), - SpringAnnotated.class)); + assertFalse(springTxAnnotatedPc.matches(SpringAnnotated.class.getMethod("foo"), SpringAnnotated.class)); } @Test @@ -154,7 +139,7 @@ private AspectJExpressionPointcut testMatchAnnotationOnClass(String expression) ajexp.setExpression(expression); assertFalse(ajexp.matches(getAge, TestBean.class)); - assertTrue(ajexp.matches(HasTransactionalAnnotation.class.getMethod("foo", (Class[]) null), HasTransactionalAnnotation.class)); + assertTrue(ajexp.matches(HasTransactionalAnnotation.class.getMethod("foo"), HasTransactionalAnnotation.class)); assertTrue(ajexp.matches(HasTransactionalAnnotation.class.getMethod("bar", String.class), HasTransactionalAnnotation.class)); assertTrue(ajexp.matches(BeanB.class.getMethod("setName", String.class), BeanB.class)); assertFalse(ajexp.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); @@ -168,10 +153,10 @@ public void testAnnotationOnMethodWithFQN() throws SecurityException, NoSuchMeth ajexp.setExpression(expression); assertFalse(ajexp.matches(getAge, TestBean.class)); - assertFalse(ajexp.matches(HasTransactionalAnnotation.class.getMethod("foo", (Class[]) null), HasTransactionalAnnotation.class)); + assertFalse(ajexp.matches(HasTransactionalAnnotation.class.getMethod("foo"), HasTransactionalAnnotation.class)); assertFalse(ajexp.matches(HasTransactionalAnnotation.class.getMethod("bar", String.class), HasTransactionalAnnotation.class)); assertFalse(ajexp.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); - assertTrue(ajexp.matches(BeanA.class.getMethod("getAge", (Class[]) null), BeanA.class)); + assertTrue(ajexp.matches(BeanA.class.getMethod("getAge"), BeanA.class)); assertFalse(ajexp.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); } @@ -182,10 +167,10 @@ public void testAnnotationOnMethodWithWildcard() throws SecurityException, NoSuc anySpringMethodAnnotation.setExpression(expression); assertFalse(anySpringMethodAnnotation.matches(getAge, TestBean.class)); - assertFalse(anySpringMethodAnnotation.matches(HasTransactionalAnnotation.class.getMethod("foo", (Class[]) null), HasTransactionalAnnotation.class)); + assertFalse(anySpringMethodAnnotation.matches(HasTransactionalAnnotation.class.getMethod("foo"), HasTransactionalAnnotation.class)); assertFalse(anySpringMethodAnnotation.matches(HasTransactionalAnnotation.class.getMethod("bar", String.class), HasTransactionalAnnotation.class)); assertFalse(anySpringMethodAnnotation.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); - assertTrue(anySpringMethodAnnotation.matches(BeanA.class.getMethod("getAge", (Class[]) null), BeanA.class)); + assertTrue(anySpringMethodAnnotation.matches(BeanA.class.getMethod("getAge"), BeanA.class)); assertFalse(anySpringMethodAnnotation.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); } @@ -196,10 +181,10 @@ public void testAnnotationOnMethodArgumentsWithFQN() throws SecurityException, N takesSpringAnnotatedArgument2.setExpression(expression); assertFalse(takesSpringAnnotatedArgument2.matches(getAge, TestBean.class)); - assertFalse(takesSpringAnnotatedArgument2.matches(HasTransactionalAnnotation.class.getMethod("foo", (Class[]) null), HasTransactionalAnnotation.class)); + assertFalse(takesSpringAnnotatedArgument2.matches(HasTransactionalAnnotation.class.getMethod("foo"), HasTransactionalAnnotation.class)); assertFalse(takesSpringAnnotatedArgument2.matches(HasTransactionalAnnotation.class.getMethod("bar", String.class), HasTransactionalAnnotation.class)); assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); - assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("getAge", (Class[]) null), BeanA.class)); + assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("getAge"), BeanA.class)); assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); assertTrue(takesSpringAnnotatedArgument2.matches( @@ -213,8 +198,7 @@ public void testAnnotationOnMethodArgumentsWithFQN() throws SecurityException, N assertFalse(takesSpringAnnotatedArgument2.matches( ProcessesSpringAnnotatedParameters.class.getMethod("takesNoAnnotatedParameters", TestBean.class, BeanA.class), - ProcessesSpringAnnotatedParameters.class, - new Object[] { new TestBean(), new BeanA()}) + ProcessesSpringAnnotatedParameters.class, new TestBean(), new BeanA()) ); } @@ -225,10 +209,10 @@ public void testAnnotationOnMethodArgumentsWithWildcards() throws SecurityExcept takesSpringAnnotatedArgument2.setExpression(expression); assertFalse(takesSpringAnnotatedArgument2.matches(getAge, TestBean.class)); - assertFalse(takesSpringAnnotatedArgument2.matches(HasTransactionalAnnotation.class.getMethod("foo", (Class[]) null), HasTransactionalAnnotation.class)); + assertFalse(takesSpringAnnotatedArgument2.matches(HasTransactionalAnnotation.class.getMethod("foo"), HasTransactionalAnnotation.class)); assertFalse(takesSpringAnnotatedArgument2.matches(HasTransactionalAnnotation.class.getMethod("bar", String.class), HasTransactionalAnnotation.class)); assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); - assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("getAge", (Class[]) null), BeanA.class)); + assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("getAge"), BeanA.class)); assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); assertTrue(takesSpringAnnotatedArgument2.matches( @@ -240,6 +224,19 @@ public void testAnnotationOnMethodArgumentsWithWildcards() throws SecurityExcept } + public static class HasGeneric { + + public void setFriends(List friends) { + } + public void setEnemies(List enemies) { + } + public void setPartners(List partners) { + } + public void setPhoneNumbers(List numbers) { + } + } + + public static class ProcessesSpringAnnotatedParameters { public void takesAnnotatedParameters(TestBean tb, SpringAnnotated sa) { 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 6d925271ce..3f34ff592e 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.aop.aspectj.annotation; import java.io.FileNotFoundException; @@ -961,7 +962,7 @@ public void recordModificationIfSetterArgumentDiffersFromOldValue(JoinPoint jp, private Method getGetterFromSetter(Method setter) { String getterName = setter.getName().replaceFirst("set", "get"); try { - return setter.getDeclaringClass().getMethod(getterName, (Class[]) null); + return setter.getDeclaringClass().getMethod(getterName); } catch (NoSuchMethodException ex) { // must be write only diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectJPointcutAdvisorTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectJPointcutAdvisorTests.java index b524e0c74b..2f84af9c41 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectJPointcutAdvisorTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectJPointcutAdvisorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author 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,18 +31,21 @@ * @author Rod Johnson * @author Chris Beams */ -public final class AspectJPointcutAdvisorTests { +public class AspectJPointcutAdvisorTests { + + private final AspectJAdvisorFactory af = new ReflectiveAspectJAdvisorFactory(); - private AspectJAdvisorFactory af = new ReflectiveAspectJAdvisorFactory(); @Test public void testSingleton() throws SecurityException, NoSuchMethodException { AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(); ajexp.setExpression(AspectJExpressionPointcutTests.MATCH_ALL_METHODS); - InstantiationModelAwarePointcutAdvisorImpl ajpa = new InstantiationModelAwarePointcutAdvisorImpl(af, ajexp, - new SingletonMetadataAwareAspectInstanceFactory(new AbstractAspectJAdvisorFactoryTests.ExceptionAspect(null),"someBean"), - TestBean.class.getMethod("getAge", (Class[]) null),1,"someBean"); + InstantiationModelAwarePointcutAdvisorImpl ajpa = new InstantiationModelAwarePointcutAdvisorImpl( + ajexp, TestBean.class.getMethod("getAge"), af, + new SingletonMetadataAwareAspectInstanceFactory(new AbstractAspectJAdvisorFactoryTests.ExceptionAspect(null), "someBean"), + 1, "someBean"); + assertSame(Pointcut.TRUE, ajpa.getAspectMetadata().getPerClausePointcut()); assertFalse(ajpa.isPerInstance()); } @@ -52,34 +55,35 @@ public void testPerTarget() throws SecurityException, NoSuchMethodException { AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(); ajexp.setExpression(AspectJExpressionPointcutTests.MATCH_ALL_METHODS); - InstantiationModelAwarePointcutAdvisorImpl ajpa = new InstantiationModelAwarePointcutAdvisorImpl(af, ajexp, - new SingletonMetadataAwareAspectInstanceFactory(new PerTargetAspect(),"someBean"), null, 1, "someBean"); + InstantiationModelAwarePointcutAdvisorImpl ajpa = new InstantiationModelAwarePointcutAdvisorImpl( + ajexp, TestBean.class.getMethod("getAge"), af, + new SingletonMetadataAwareAspectInstanceFactory(new PerTargetAspect(), "someBean"), + 1, "someBean"); + assertNotSame(Pointcut.TRUE, ajpa.getAspectMetadata().getPerClausePointcut()); assertTrue(ajpa.getAspectMetadata().getPerClausePointcut() instanceof AspectJExpressionPointcut); assertTrue(ajpa.isPerInstance()); assertTrue(ajpa.getAspectMetadata().getPerClausePointcut().getClassFilter().matches(TestBean.class)); assertFalse(ajpa.getAspectMetadata().getPerClausePointcut().getMethodMatcher().matches( - TestBean.class.getMethod("getAge", (Class[]) null), - TestBean.class)); + TestBean.class.getMethod("getAge"), TestBean.class)); assertTrue(ajpa.getAspectMetadata().getPerClausePointcut().getMethodMatcher().matches( - TestBean.class.getMethod("getSpouse", (Class[]) null), - TestBean.class)); + TestBean.class.getMethod("getSpouse"), TestBean.class)); } - @Test(expected=AopConfigException.class) + @Test(expected = AopConfigException.class) public void testPerCflowTarget() { testIllegalInstantiationModel(AbstractAspectJAdvisorFactoryTests.PerCflowAspect.class); } - @Test(expected=AopConfigException.class) + @Test(expected = AopConfigException.class) public void testPerCflowBelowTarget() { testIllegalInstantiationModel(AbstractAspectJAdvisorFactoryTests.PerCflowBelowAspect.class); } private void testIllegalInstantiationModel(Class c) throws AopConfigException { - new AspectMetadata(c,"someBean"); + new AspectMetadata(c, "someBean"); } } diff --git a/spring-aop/src/test/java/org/springframework/aop/framework/MethodInvocationTests.java b/spring-aop/src/test/java/org/springframework/aop/framework/MethodInvocationTests.java index 7083320483..2a7f726fb7 100644 --- a/spring-aop/src/test/java/org/springframework/aop/framework/MethodInvocationTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/framework/MethodInvocationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ public final class MethodInvocationTests { @Test public void testValidInvocation() throws Throwable { - Method m = Object.class.getMethod("hashCode", (Class[]) null); + Method m = Object.class.getMethod("hashCode"); Object proxy = new Object(); final Object returnValue = new Object(); List is = new LinkedList(); @@ -67,7 +67,7 @@ public String toString() { }; List is = new LinkedList(); - Method m = Object.class.getMethod("hashCode", (Class[]) null); + Method m = Object.class.getMethod("hashCode"); Object proxy = new Object(); ReflectiveMethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, m, null, null, is); diff --git a/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java b/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java index 25e35f5783..6cb0c47cf5 100644 --- a/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,7 +78,7 @@ public void testCorrectHandlerUsed() throws Throwable { ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th); FileNotFoundException ex = new FileNotFoundException(); MethodInvocation mi = mock(MethodInvocation.class); - given(mi.getMethod()).willReturn(Object.class.getMethod("hashCode", (Class[]) null)); + given(mi.getMethod()).willReturn(Object.class.getMethod("hashCode")); given(mi.getThis()).willReturn(new Object()); given(mi.proceed()).willThrow(ex); try { @@ -140,12 +140,15 @@ public void afterThrowing(RemoteException ex) throws Throwable { assertEquals(1, th.getCalls("remoteException")); } + @SuppressWarnings("serial") static class MyThrowsHandler extends MethodCounter implements ThrowsAdvice { + // Full method signature public void afterThrowing(Method m, Object[] args, Object target, IOException ex) { count("ioException"); } + public void afterThrowing(RemoteException ex) throws Throwable { count("remoteException"); } diff --git a/spring-aop/src/test/java/org/springframework/aop/support/AbstractRegexpMethodPointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/support/AbstractRegexpMethodPointcutTests.java index 64ab845a04..39cbed336e 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/AbstractRegexpMethodPointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/AbstractRegexpMethodPointcutTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author 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,8 +54,8 @@ public void testSerializationWithNoPatternSupplied() throws Exception { } protected void noPatternSuppliedTests(AbstractRegexpMethodPointcut rpc) throws Exception { - assertFalse(rpc.matches(Object.class.getMethod("hashCode", (Class[]) null), String.class)); - assertFalse(rpc.matches(Object.class.getMethod("wait", (Class[]) null), Object.class)); + assertFalse(rpc.matches(Object.class.getMethod("hashCode"), String.class)); + assertFalse(rpc.matches(Object.class.getMethod("wait"), Object.class)); assertEquals(0, rpc.getPatterns().length); } @@ -69,38 +69,38 @@ public void testExactMatch() throws Exception { protected void exactMatchTests(AbstractRegexpMethodPointcut rpc) throws Exception { // assumes rpc.setPattern("java.lang.Object.hashCode"); - assertTrue(rpc.matches(Object.class.getMethod("hashCode", (Class[]) null), String.class)); - assertTrue(rpc.matches(Object.class.getMethod("hashCode", (Class[]) null), Object.class)); - assertFalse(rpc.matches(Object.class.getMethod("wait", (Class[]) null), Object.class)); + assertTrue(rpc.matches(Object.class.getMethod("hashCode"), String.class)); + assertTrue(rpc.matches(Object.class.getMethod("hashCode"), Object.class)); + assertFalse(rpc.matches(Object.class.getMethod("wait"), Object.class)); } @Test public void testSpecificMatch() throws Exception { rpc.setPattern("java.lang.String.hashCode"); - assertTrue(rpc.matches(Object.class.getMethod("hashCode", (Class[]) null), String.class)); - assertFalse(rpc.matches(Object.class.getMethod("hashCode", (Class[]) null), Object.class)); + assertTrue(rpc.matches(Object.class.getMethod("hashCode"), String.class)); + assertFalse(rpc.matches(Object.class.getMethod("hashCode"), Object.class)); } @Test public void testWildcard() throws Exception { rpc.setPattern(".*Object.hashCode"); - assertTrue(rpc.matches(Object.class.getMethod("hashCode", (Class[]) null), Object.class)); - assertFalse(rpc.matches(Object.class.getMethod("wait", (Class[]) null), Object.class)); + assertTrue(rpc.matches(Object.class.getMethod("hashCode"), Object.class)); + assertFalse(rpc.matches(Object.class.getMethod("wait"), Object.class)); } @Test public void testWildcardForOneClass() throws Exception { rpc.setPattern("java.lang.Object.*"); - assertTrue(rpc.matches(Object.class.getMethod("hashCode", (Class[]) null), String.class)); - assertTrue(rpc.matches(Object.class.getMethod("wait", (Class[]) null), String.class)); + assertTrue(rpc.matches(Object.class.getMethod("hashCode"), String.class)); + assertTrue(rpc.matches(Object.class.getMethod("wait"), String.class)); } @Test public void testMatchesObjectClass() throws Exception { rpc.setPattern("java.lang.Object.*"); - assertTrue(rpc.matches(Exception.class.getMethod("hashCode", (Class[]) null), IOException.class)); + assertTrue(rpc.matches(Exception.class.getMethod("hashCode"), IOException.class)); // Doesn't match a method from Throwable - assertFalse(rpc.matches(Exception.class.getMethod("getMessage", (Class[]) null), Exception.class)); + assertFalse(rpc.matches(Exception.class.getMethod("getMessage"), Exception.class)); } @Test diff --git a/spring-aop/src/test/java/org/springframework/aop/support/ComposablePointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/support/ComposablePointcutTests.java index b229c3ee59..b34438ee3d 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/ComposablePointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/ComposablePointcutTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,7 @@ * @author Rod Johnson * @author Chris Beams */ -public final class ComposablePointcutTests { +public class ComposablePointcutTests { public static MethodMatcher GETTER_METHOD_MATCHER = new StaticMethodMatcher() { @Override @@ -62,11 +62,12 @@ public boolean matches(Method m, Class targetClass) { } }; + @Test public void testMatchAll() throws NoSuchMethodException { Pointcut pc = new ComposablePointcut(); assertTrue(pc.getClassFilter().matches(Object.class)); - assertTrue(pc.getMethodMatcher().matches(Object.class.getMethod("hashCode", (Class[]) null), Exception.class)); + assertTrue(pc.getMethodMatcher().matches(Object.class.getMethod("hashCode"), Exception.class)); } @Test @@ -93,23 +94,23 @@ public void testFilterByClass() throws NoSuchMethodException { public void testUnionMethodMatcher() { // Matches the getAge() method in any class ComposablePointcut pc = new ComposablePointcut(ClassFilter.TRUE, GET_AGE_METHOD_MATCHER); - assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class, null)); - assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class, null)); + assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class)); + assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class)); pc.union(GETTER_METHOD_MATCHER); // Should now match all getter methods - assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class, null)); - assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class, null)); - assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class, null)); + assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class)); + assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class)); + assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class)); pc.union(ABSQUATULATE_METHOD_MATCHER); // Should now match absquatulate() as well - assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class, null)); - assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class, null)); - assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class, null)); + assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class)); + assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class)); + assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class)); // But it doesn't match everything - assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_SET_AGE, TestBean.class, null)); + assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_SET_AGE, TestBean.class)); } @Test @@ -124,9 +125,9 @@ public void testIntersectionMethodMatcher() { assertTrue(pc.getMethodMatcher().matches(PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class)); pc.intersection(GET_AGE_METHOD_MATCHER); // Use the Pointcuts matches method - assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class, null)); - assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class, null)); + assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class)); + assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class)); } @Test @@ -153,4 +154,5 @@ public void testEqualsAndHashCode() throws Exception { assertEquals(pc1, pc2); assertEquals(pc1.hashCode(), pc2.hashCode()); } + } diff --git a/spring-aop/src/test/java/org/springframework/aop/support/MethodMatchersTests.java b/spring-aop/src/test/java/org/springframework/aop/support/MethodMatchersTests.java index afa1726b01..5b12fa19e9 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/MethodMatchersTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/MethodMatchersTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,7 @@ * @author Juergen Hoeller * @author Chris Beams */ -public final class MethodMatchersTests { +public class MethodMatchersTests { private final Method EXCEPTION_GETMESSAGE; @@ -44,10 +44,10 @@ public final class MethodMatchersTests { public MethodMatchersTests() throws Exception { - EXCEPTION_GETMESSAGE = Exception.class.getMethod("getMessage", (Class[]) null); - ITESTBEAN_GETAGE = ITestBean.class.getMethod("getAge", (Class[]) null); - ITESTBEAN_SETAGE = ITestBean.class.getMethod("setAge", new Class[] { int.class }); - IOTHER_ABSQUATULATE = IOther.class.getMethod("absquatulate", (Class[]) null); + EXCEPTION_GETMESSAGE = Exception.class.getMethod("getMessage"); + ITESTBEAN_GETAGE = ITestBean.class.getMethod("getAge"); + ITESTBEAN_SETAGE = ITestBean.class.getMethod("setAge", int.class); + IOTHER_ABSQUATULATE = IOther.class.getMethod("absquatulate"); } @@ -82,12 +82,12 @@ public void testDynamicAndStaticMethodMatcherIntersection() throws Exception { MethodMatcher intersection = MethodMatchers.intersection(mm1, mm2); assertTrue("Intersection is a dynamic matcher", intersection.isRuntime()); assertTrue("2Matched setAge method", intersection.matches(ITESTBEAN_SETAGE, TestBean.class)); - assertTrue("3Matched setAge method", intersection.matches(ITESTBEAN_SETAGE, TestBean.class, new Object[] { new Integer(5) })); + assertTrue("3Matched setAge method", intersection.matches(ITESTBEAN_SETAGE, TestBean.class, new Integer(5))); // Knock out dynamic part intersection = MethodMatchers.intersection(intersection, new TestDynamicMethodMatcherWhichDoesNotMatch()); assertTrue("Intersection is a dynamic matcher", intersection.isRuntime()); assertTrue("2Matched setAge method", intersection.matches(ITESTBEAN_SETAGE, TestBean.class)); - assertFalse("3 - not Matched setAge method", intersection.matches(ITESTBEAN_SETAGE, TestBean.class, new Object[] { new Integer(5) })); + assertFalse("3 - not Matched setAge method", intersection.matches(ITESTBEAN_SETAGE, TestBean.class, new Integer(5))); } @Test @@ -125,18 +125,20 @@ public boolean matches(Method m, Class targetClass) { } } + private static class TestDynamicMethodMatcherWhichMatches extends DynamicMethodMatcher { @Override - public boolean matches(Method m, Class targetClass, Object[] args) { + public boolean matches(Method m, Class targetClass, Object... args) { return true; } } + private static class TestDynamicMethodMatcherWhichDoesNotMatch extends DynamicMethodMatcher { @Override - public boolean matches(Method m, Class targetClass, Object[] args) { + public boolean matches(Method m, Class targetClass, Object... args) { return false; } } diff --git a/spring-aop/src/test/java/org/springframework/aop/support/PointcutsTests.java b/spring-aop/src/test/java/org/springframework/aop/support/PointcutsTests.java index 6404c8d760..5e5569a949 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/PointcutsTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/PointcutsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author 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 @@ * @author Rod Johnson * @author Chris Beams */ -public final class PointcutsTests { +public class PointcutsTests { public static Method TEST_BEAN_SET_AGE; public static Method TEST_BEAN_GET_AGE; @@ -39,10 +39,10 @@ public final class PointcutsTests { static { try { - TEST_BEAN_SET_AGE = TestBean.class.getMethod("setAge", new Class[] { int.class }); - TEST_BEAN_GET_AGE = TestBean.class.getMethod("getAge", (Class[]) null); - TEST_BEAN_GET_NAME = TestBean.class.getMethod("getName", (Class[]) null); - TEST_BEAN_ABSQUATULATE = TestBean.class.getMethod("absquatulate", (Class[]) null); + TEST_BEAN_SET_AGE = TestBean.class.getMethod("setAge", int.class); + TEST_BEAN_GET_AGE = TestBean.class.getMethod("getAge"); + TEST_BEAN_GET_NAME = TestBean.class.getMethod("getName"); + TEST_BEAN_ABSQUATULATE = TestBean.class.getMethod("absquatulate"); } catch (Exception ex) { throw new RuntimeException("Shouldn't happen: error in test suite"); @@ -125,22 +125,22 @@ public boolean matches(Method m, Class targetClass) { @Test public void testTrue() { - assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); - assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); + assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_GET_AGE, TestBean.class)); + assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_ABSQUATULATE, TestBean.class)); + assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_GET_AGE, TestBean.class)); + assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_ABSQUATULATE, TestBean.class)); } @Test public void testMatches() { - assertTrue(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertFalse(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); - assertFalse(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertTrue(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); + assertTrue(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertFalse(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_ABSQUATULATE, TestBean.class)); + assertFalse(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertTrue(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_ABSQUATULATE, TestBean.class)); } /** @@ -149,29 +149,29 @@ public void testMatches() { @Test public void testUnionOfSettersAndGetters() { Pointcut union = Pointcuts.union(allClassGetterPointcut, allClassSetterPointcut); - assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class)); } @Test public void testUnionOfSpecificGetters() { Pointcut union = Pointcuts.union(allClassGetAgePointcut, allClassGetNamePointcut); - assertFalse(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_NAME, TestBean.class)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_NAME, TestBean.class)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class)); // Union with all setters union = Pointcuts.union(union, allClassSetterPointcut); - assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_NAME, TestBean.class)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_NAME, TestBean.class)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class)); - assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); + assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); } /** @@ -180,16 +180,16 @@ public void testUnionOfSpecificGetters() { */ @Test public void testUnionOfAllSettersAndSubclassSetters() { - assertFalse(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertTrue(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_SET_AGE, MyTestBean.class, new Object[] { new Integer(6)})); - assertFalse(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_GET_AGE, TestBean.class, null)); + assertFalse(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertTrue(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_SET_AGE, MyTestBean.class, new Integer(6))); + assertFalse(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_GET_AGE, TestBean.class)); Pointcut union = Pointcuts.union(myTestBeanSetterPointcut, allClassGetterPointcut); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, MyTestBean.class, null)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, MyTestBean.class)); // Still doesn't match superclass setter - assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, MyTestBean.class, new Object[] { new Integer(6)})); - assertFalse(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); + assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, MyTestBean.class, new Integer(6))); + assertFalse(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); } /** @@ -198,44 +198,44 @@ public void testUnionOfAllSettersAndSubclassSetters() { */ @Test public void testIntersectionOfSpecificGettersAndSubclassGetters() { - assertTrue(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertTrue(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_AGE, MyTestBean.class, null)); - assertFalse(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertFalse(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertTrue(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_NAME, MyTestBean.class, null)); - assertTrue(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_AGE, MyTestBean.class, null)); + assertTrue(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_AGE, TestBean.class)); + assertTrue(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_AGE, MyTestBean.class)); + assertFalse(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_NAME, TestBean.class)); + assertFalse(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_AGE, TestBean.class)); + assertTrue(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_NAME, MyTestBean.class)); + assertTrue(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_AGE, MyTestBean.class)); Pointcut intersection = Pointcuts.intersection(allClassGetAgePointcut, myTestBeanGetterPointcut); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBean.class, null)); - assertTrue(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBean.class, null)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, TestBean.class)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBean.class)); + assertTrue(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBean.class)); // Matches subclass of MyTestBean - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBeanSubclass.class, null)); - assertTrue(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBeanSubclass.class, null)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBeanSubclass.class)); + assertTrue(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBeanSubclass.class)); // Now intersection with MyTestBeanSubclass getters should eliminate MyTestBean target intersection = Pointcuts.intersection(intersection, myTestBeanSubclassGetterPointcut); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBean.class, null)); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBean.class, null)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, TestBean.class)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBean.class)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBean.class)); // Still matches subclass of MyTestBean - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBeanSubclass.class, null)); - assertTrue(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBeanSubclass.class, null)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBeanSubclass.class)); + assertTrue(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBeanSubclass.class)); // Now union with all TestBean methods Pointcut union = Pointcuts.union(intersection, allTestBeanMethodsPointcut); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(union, TEST_BEAN_GET_NAME, MyTestBean.class, null)); - assertFalse(Pointcuts.matches(union, TEST_BEAN_GET_AGE, MyTestBean.class, null)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_NAME, TestBean.class)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_GET_NAME, MyTestBean.class)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_GET_AGE, MyTestBean.class)); // Still matches subclass of MyTestBean - assertFalse(Pointcuts.matches(union, TEST_BEAN_GET_NAME, MyTestBeanSubclass.class, null)); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, MyTestBeanSubclass.class, null)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_GET_NAME, MyTestBeanSubclass.class)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, MyTestBeanSubclass.class)); - assertTrue(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); - assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, MyTestBean.class, null)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, MyTestBean.class)); } @@ -245,9 +245,9 @@ public void testIntersectionOfSpecificGettersAndSubclassGetters() { @Test public void testSimpleIntersection() { Pointcut intersection = Pointcuts.intersection(allClassGetterPointcut, allClassSetterPointcut); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_ABSQUATULATE, TestBean.class)); } } diff --git a/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java b/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java index 95dff09cc0..de49c8af7f 100644 --- a/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java +++ b/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java @@ -1,6 +1,5 @@ - /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author 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,28 +28,22 @@ public class NopInterceptor implements MethodInterceptor { private int count; - /** - * @see org.aopalliance.intercept.MethodInterceptor#invoke(MethodInvocation) - */ + @Override public Object invoke(MethodInvocation invocation) throws Throwable { increment(); return invocation.proceed(); } - public int getCount() { - return this.count; - } - protected void increment() { - ++count; + this.count++; } - @Override - public int hashCode() { - return 0; + public int getCount() { + return this.count; } + @Override public boolean equals(Object other) { if (!(other instanceof NopInterceptor)) { @@ -62,5 +55,9 @@ public boolean equals(Object other) { return this.count == ((NopInterceptor) other).count; } + @Override + public int hashCode() { + return NopInterceptor.class.hashCode(); + } } diff --git a/spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java b/spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java index 805dabd41a..bfa856144a 100644 --- a/spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java +++ b/spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author 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,31 +28,29 @@ @SuppressWarnings("serial") public class SerializablePerson implements Person, Serializable { - private static final long serialVersionUID = 1L; - - private String name; private int age; + @Override - public int getAge() { - return age; + public String getName() { + return name; } @Override - public void setAge(int age) { - this.age = age; + public void setName(String name) { + this.name = name; } @Override - public String getName() { - return name; + public int getAge() { + return age; } @Override - public void setName(String name) { - this.name = name; + public void setAge(int age) { + this.age = age; } @Override @@ -63,10 +61,6 @@ public Object echo(Object o) throws Throwable { return o; } - @Override - public int hashCode() { - return 0; - } @Override public boolean equals(Object other) { @@ -77,4 +71,9 @@ public boolean equals(Object other) { return p.age == age && ObjectUtils.nullSafeEquals(name, p.name); } + @Override + public int hashCode() { + return SerializablePerson.class.hashCode(); + } + } 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 b1aa1791d1..9932c79d12 100644 --- a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJCachingConfiguration.java +++ b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJCachingConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author 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,11 @@ import org.springframework.context.annotation.Role; /** - * {@code @Configuration} class that registers the Spring infrastructure beans necessary - * to enable AspectJ-based annotation-driven cache management. + * {@code @Configuration} class that registers the Spring infrastructure beans + * necessary to enable AspectJ-based annotation-driven cache management. * * @author Chris Beams + * @author Stephane Nicoll * @since 3.1 * @see org.springframework.cache.annotation.EnableCaching * @see org.springframework.cache.annotation.CachingConfigurationSelector @@ -39,12 +40,18 @@ public class AspectJCachingConfiguration extends AbstractCachingConfiguration { @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public AnnotationCacheAspect cacheAspect() { AnnotationCacheAspect cacheAspect = AnnotationCacheAspect.aspectOf(); - if (this.cacheManager != null) { + 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); + } 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 747f280f27..1d141b2ee9 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 @@ -29,8 +29,8 @@ * Configurable}. * *

This configuration class is automatically imported when using the - * @{@link EnableSpringConfigured} annotation. See {@code @EnableSpringConfigured}'s - * javadoc for complete usage details. + * {@link EnableSpringConfigured @EnableSpringConfigured} annotation. See + * {@code @EnableSpringConfigured}'s javadoc for complete usage details. * * @author Chris Beams * @since 3.1 diff --git a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj index 684eaf9e1c..9efd0a4b93 100644 --- a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author 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,47 +17,49 @@ package org.springframework.scheduling.aspectj; import java.util.concurrent.Callable; -import java.util.concurrent.Executor; import java.util.concurrent.Future; import org.aspectj.lang.annotation.SuppressAjWarnings; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.aop.interceptor.AsyncExecutionAspectSupport; -import org.springframework.core.task.AsyncListenableTaskExecutor; import org.springframework.core.task.AsyncTaskExecutor; -import org.springframework.util.concurrent.ListenableFuture; /** * Abstract aspect that routes selected methods asynchronously. * - *

This aspect needs to be injected with an implementation of - * {@link Executor} to activate it for a specific thread pool. - * Otherwise it will simply delegate all calls synchronously. + *

This aspect needs to be injected with an implementation of a task-oriented + * {@link java.util.concurrent.Executor} to activate it for a specific thread pool, + * or with a {@link org.springframework.beans.factory.BeanFactory} for default + * executor lookup. Otherwise it will simply delegate all calls synchronously. * * @author Ramnivas Laddad * @author Juergen Hoeller * @author Chris Beams * @author Stephane Nicoll * @since 3.0.5 + * @see #setExecutor + * @see #setBeanFactory + * @see #getDefaultExecutor */ public abstract aspect AbstractAsyncExecutionAspect extends AsyncExecutionAspectSupport { /** - * Create an {@code AnnotationAsyncExecutionAspect} with a {@code null} default - * executor, which should instead be set via {@code #aspectOf} and - * {@link #setExecutor(Executor)}. The same applies for {@link #setExceptionHandler} + * Create an {@code AnnotationAsyncExecutionAspect} with a {@code null} + * default executor, which should instead be set via {@code #aspectOf} and + * {@link #setExecutor}. The same applies for {@link #setExceptionHandler}. */ public AbstractAsyncExecutionAspect() { super(null); } + /** * Apply around advice to methods matching the {@link #asyncMethod()} pointcut, * submit the actual calling of the method to the correct task executor and return * immediately to the caller. - * @return {@link Future} if the original method returns {@code Future}; {@code null} - * otherwise. + * @return {@link Future} if the original method returns {@code Future}; + * {@code null} otherwise */ @SuppressAjWarnings("adviceDidNotMatch") Object around() : asyncMethod() { diff --git a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj index 6df1662a19..e1a1fd9705 100644 --- a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author 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,9 +33,17 @@ import org.springframework.scheduling.annotation.Async; * on the return type. If, however, a class marked with {@code @Async} contains a method * that violates this constraint, it produces only a warning. * + *

This aspect needs to be injected with an implementation of a task-oriented + * {@link java.util.concurrent.Executor} to activate it for a specific thread pool, + * or with a {@link org.springframework.beans.factory.BeanFactory} for default + * executor lookup. Otherwise it will simply delegate all calls synchronously. + * * @author Ramnivas Laddad * @author Chris Beams * @since 3.0.5 + * @see #setExecutor + * @see #setBeanFactory + * @see #getDefaultExecutor */ public aspect AnnotationAsyncExecutionAspect extends AbstractAsyncExecutionAspect { @@ -74,7 +82,6 @@ public aspect AnnotationAsyncExecutionAspect extends AbstractAsyncExecutionAspec declare warning: execution(!(void || Future+) (@Async *).*(..)): - "Methods in a class marked with @Async that do not return void or Future will " + - "be routed synchronously"; + "Methods in a class marked with @Async that do not return void or Future will be routed synchronously"; } 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 new file mode 100644 index 0000000000..15680c9ac6 --- /dev/null +++ b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java @@ -0,0 +1,285 @@ +/* + * Copyright 2002-2016 the original author 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.cache.aspectj; + +import org.junit.After; +import org.junit.Ignore; +import org.junit.Test; + +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.cache.CacheManager; +import org.springframework.cache.CacheTestUtils; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.config.AnnotatedClassCacheableService; +import org.springframework.cache.config.CacheableService; +import org.springframework.cache.config.DefaultCacheableService; +import org.springframework.cache.config.SomeCustomKeyGenerator; +import org.springframework.cache.config.SomeKeyGenerator; +import org.springframework.cache.interceptor.CacheErrorHandler; +import org.springframework.cache.interceptor.CacheResolver; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.cache.interceptor.NamedCacheResolver; +import org.springframework.cache.interceptor.SimpleCacheErrorHandler; +import org.springframework.cache.interceptor.SimpleCacheResolver; +import org.springframework.cache.support.NoOpCacheManager; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AdviceMode; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.junit.Assert.*; + +/** + * @author Stephane Nicoll + */ +public class AspectJEnableCachingIsolatedTests { + + private ConfigurableApplicationContext ctx; + + + private void load(Class... config) { + this.ctx = new AnnotationConfigApplicationContext(config); + } + + @After + public void closeContext() { + if (this.ctx != null) { + this.ctx.close(); + } + } + + + @Test + public void testKeyStrategy() { + load(EnableCachingConfig.class); + AnnotationCacheAspect aspect = this.ctx.getBean(AnnotationCacheAspect.class); + assertSame(this.ctx.getBean("keyGenerator", KeyGenerator.class), aspect.getKeyGenerator()); + } + + @Test + public void testCacheErrorHandler() { + load(EnableCachingConfig.class); + AnnotationCacheAspect aspect = this.ctx.getBean(AnnotationCacheAspect.class); + assertSame(this.ctx.getBean("errorHandler", CacheErrorHandler.class), aspect.getErrorHandler()); + } + + + // --- local tests ------- + + @Test + public void singleCacheManagerBean() { + load(SingleCacheManagerConfig.class); + } + + @Test + public void multipleCacheManagerBeans() { + try { + load(MultiCacheManagerConfig.class); + } + catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("bean of type CacheManager")); + } + } + + @Test + public void multipleCacheManagerBeans_implementsCachingConfigurer() { + load(MultiCacheManagerConfigurer.class); // does not throw + } + + @Test + public void multipleCachingConfigurers() { + try { + load(MultiCacheManagerConfigurer.class, EnableCachingConfig.class); + } + catch (BeanCreationException ex) { + Throwable root = ex.getRootCause(); + assertTrue(root instanceof IllegalStateException); + assertTrue(ex.getMessage().contains("implementations of CachingConfigurer")); + } + } + + @Test + public void noCacheManagerBeans() { + try { + load(EmptyConfig.class); + } + catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("No bean of type CacheManager")); + } + } + + @Test + @Ignore("AspectJ has some sort of caching that makes this one fail") + public void emptyConfigSupport() { + load(EmptyConfigSupportConfig.class); + AnnotationCacheAspect aspect = this.ctx.getBean(AnnotationCacheAspect.class); + assertNotNull(aspect.getCacheResolver()); + assertEquals(SimpleCacheResolver.class, aspect.getCacheResolver().getClass()); + assertSame(this.ctx.getBean(CacheManager.class), + ((SimpleCacheResolver) aspect.getCacheResolver()).getCacheManager()); + } + + @Test + public void bothSetOnlyResolverIsUsed() { + load(FullCachingConfig.class); + + AnnotationCacheAspect aspect = this.ctx.getBean(AnnotationCacheAspect.class); + assertSame(this.ctx.getBean("cacheResolver"), aspect.getCacheResolver()); + assertSame(this.ctx.getBean("keyGenerator"), aspect.getKeyGenerator()); + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class EnableCachingConfig extends CachingConfigurerSupport { + + @Override + @Bean + public CacheManager cacheManager() { + return CacheTestUtils.createSimpleCacheManager("testCache", "primary", "secondary"); + } + + @Bean + public CacheableService service() { + return new DefaultCacheableService(); + } + + @Bean + public CacheableService classService() { + return new AnnotatedClassCacheableService(); + } + + @Override + @Bean + public KeyGenerator keyGenerator() { + return new SomeKeyGenerator(); + } + + @Override + @Bean + public CacheErrorHandler errorHandler() { + return new SimpleCacheErrorHandler(); + } + + @Bean + public KeyGenerator customKeyGenerator() { + return new SomeCustomKeyGenerator(); + } + + @Bean + public CacheManager customCacheManager() { + return CacheTestUtils.createSimpleCacheManager("testCache"); + } + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class EmptyConfig { + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class SingleCacheManagerConfig { + + @Bean + public CacheManager cm1() { + return new NoOpCacheManager(); + } + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class MultiCacheManagerConfig { + + @Bean + public CacheManager cm1() { + return new NoOpCacheManager(); + } + + @Bean + public CacheManager cm2() { + return new NoOpCacheManager(); + } + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class MultiCacheManagerConfigurer extends CachingConfigurerSupport { + + @Bean + public CacheManager cm1() { + return new NoOpCacheManager(); + } + + @Bean + public CacheManager cm2() { + return new NoOpCacheManager(); + } + + @Override + public CacheManager cacheManager() { + return cm1(); + } + + @Override + public KeyGenerator keyGenerator() { + return null; + } + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class EmptyConfigSupportConfig extends CachingConfigurerSupport { + + @Bean + public CacheManager cm() { + return new NoOpCacheManager(); + } + + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class FullCachingConfig extends CachingConfigurerSupport { + + @Override + @Bean + public CacheManager cacheManager() { + return new NoOpCacheManager(); + } + + @Override + @Bean + public KeyGenerator keyGenerator() { + return new SomeKeyGenerator(); + } + + @Override + @Bean + public CacheResolver cacheResolver() { + return new NamedCacheResolver(cacheManager(), "foo"); + } + } +} diff --git a/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingTests.java b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingTests.java new file mode 100644 index 0000000000..a8ae57d20a --- /dev/null +++ b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingTests.java @@ -0,0 +1,92 @@ +/* + * Copyright 2002-2016 the original author 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.cache.aspectj; + +import org.springframework.cache.CacheManager; +import org.springframework.cache.CacheTestUtils; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.config.AbstractCacheAnnotationTests; +import org.springframework.cache.config.AnnotatedClassCacheableService; +import org.springframework.cache.config.CacheableService; +import org.springframework.cache.config.DefaultCacheableService; +import org.springframework.cache.config.SomeCustomKeyGenerator; +import org.springframework.cache.config.SomeKeyGenerator; +import org.springframework.cache.interceptor.CacheErrorHandler; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.cache.interceptor.SimpleCacheErrorHandler; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AdviceMode; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Stephane Nicoll + */ +public class AspectJEnableCachingTests extends AbstractCacheAnnotationTests { + + @Override + protected ConfigurableApplicationContext getApplicationContext() { + return new AnnotationConfigApplicationContext(EnableCachingConfig.class); + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class EnableCachingConfig extends CachingConfigurerSupport { + + @Override + @Bean + public CacheManager cacheManager() { + return CacheTestUtils.createSimpleCacheManager("testCache", "primary", "secondary"); + } + + @Bean + public CacheableService service() { + return new DefaultCacheableService(); + } + + @Bean + public CacheableService classService() { + return new AnnotatedClassCacheableService(); + } + + @Override + @Bean + public KeyGenerator keyGenerator() { + return new SomeKeyGenerator(); + } + + @Override + @Bean + public CacheErrorHandler errorHandler() { + return new SimpleCacheErrorHandler(); + } + + @Bean + public KeyGenerator customKeyGenerator() { + return new SomeCustomKeyGenerator(); + } + + @Bean + public CacheManager customCacheManager() { + return CacheTestUtils.createSimpleCacheManager("testCache"); + } + } + +} diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java index 224f7ffa95..cebb67883b 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -177,7 +177,7 @@ public Object multiCache(Object arg1) { } @Override - @Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames = "secondary", key = "#p0") }) + @Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames = "secondary", key = "#a0"), @CacheEvict(cacheNames = "primary", key = "#p0 + 'A'") }) public Object multiEvict(Object arg1) { return counter.getAndIncrement(); } @@ -189,7 +189,7 @@ public Object multiCacheAndEvict(Object arg1) { } @Override - @Caching(cacheable = { @Cacheable(cacheNames = "primary", condition = "#p0 == 3") }, evict = { @CacheEvict("secondary") }) + @Caching(cacheable = { @Cacheable(cacheNames = "primary", condition = "#a0 == 3") }, evict = { @CacheEvict("secondary") }) public Object multiConditionalCacheAndEvict(Object arg1) { return counter.getAndIncrement(); } diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java index c2c923f531..d539075e0d 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -185,8 +185,7 @@ public Long multiCache(Object arg1) { } @Override -//FIXME @Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames = "secondary", key = "#p0"), @CacheEvict(cacheNames = "primary", key = "#p0 + 'A'") }) - @Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames = "secondary", key = "#p0") }) + @Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames = "secondary", key = "#p0"), @CacheEvict(cacheNames = "primary", key = "#p0 + 'A'") }) public Long multiEvict(Object arg1) { return counter.getAndIncrement(); } 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 8db104f0cc..d817b205d3 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-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ * * @author Stephane Nicoll */ -final class SomeCustomKeyGenerator implements KeyGenerator { +public final class SomeCustomKeyGenerator implements KeyGenerator { @Override public Object generate(Object target, Method method, Object... params) { diff --git a/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java b/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java index bcbe1985d1..43d6823331 100644 --- a/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java +++ b/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java @@ -31,7 +31,6 @@ import groovy.lang.GroovyShell; import groovy.lang.GroovySystem; import groovy.lang.MetaClass; - import org.codehaus.groovy.runtime.DefaultGroovyMethods; import org.codehaus.groovy.runtime.InvokerHelper; @@ -337,8 +336,8 @@ public void xmlns(Map definition) { if (uri == null) { throw new IllegalArgumentException("Namespace definition must supply a non-null URI"); } - NamespaceHandler namespaceHandler = this.groovyDslXmlBeanDefinitionReader.getNamespaceHandlerResolver().resolve( - uri); + NamespaceHandler namespaceHandler = + this.groovyDslXmlBeanDefinitionReader.getNamespaceHandlerResolver().resolve(uri); if (namespaceHandler == null) { throw new BeanDefinitionParsingException(new Problem("No namespace handler found for URI: " + uri, new Location(new DescriptiveResource(("Groovy"))))); @@ -375,7 +374,7 @@ else if ("ref".equals(name)) { 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(); + refName = ((RuntimeBeanReference) args[0]).getBeanName(); } else { refName = args[0].toString(); @@ -388,7 +387,7 @@ else if ("ref".equals(name)) { } return new RuntimeBeanReference(refName, parentRef); } - else if (this.namespaces.containsKey(name) && args.length > 0 && (args[0] instanceof Closure)) { + else if (this.namespaces.containsKey(name) && args.length > 0 && args[0] instanceof Closure) { GroovyDynamicElementReader reader = createDynamicElementReader(name); reader.invokeMethod("doCall", args); } @@ -396,7 +395,8 @@ else if (args.length > 0 && args[0] instanceof Closure) { // abstract bean definition return invokeBeanDefiningMethod(name, args); } - else if (args.length > 0 && (args[0] instanceof Class || args[0] instanceof RuntimeBeanReference || args[0] instanceof Map)) { + else if (args.length > 0 && + (args[0] instanceof Class || args[0] instanceof RuntimeBeanReference || args[0] instanceof Map)) { return invokeBeanDefiningMethod(name, args); } else if (args.length > 1 && args[args.length -1] instanceof Closure) { 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 12b304666c..78053009d1 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-2015 the original author or authors. + * Copyright 2002-2016 the original author 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,10 +87,10 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA } } + private int autoGrowCollectionLimit = Integer.MAX_VALUE; - /** The wrapped object */ - private Object object; + private Object wrappedObject; private String nestedPath = ""; @@ -204,23 +204,23 @@ public void setWrappedInstance(Object object) { public void setWrappedInstance(Object object, String nestedPath, Object rootObject) { Assert.notNull(object, "Target object must not be null"); if (object.getClass() == javaUtilOptionalClass) { - this.object = OptionalUnwrapper.unwrap(object); + this.wrappedObject = OptionalUnwrapper.unwrap(object); } else { - this.object = object; + this.wrappedObject = object; } this.nestedPath = (nestedPath != null ? nestedPath : ""); - this.rootObject = (!"".equals(this.nestedPath) ? rootObject : this.object); + this.rootObject = (!"".equals(this.nestedPath) ? rootObject : this.wrappedObject); this.nestedPropertyAccessors = null; - this.typeConverterDelegate = new TypeConverterDelegate(this, this.object); + this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject); } public final Object getWrappedInstance() { - return this.object; + return this.wrappedObject; } public final Class getWrappedClass() { - return (this.object != null ? this.object.getClass() : null); + return (this.wrappedObject != null ? this.wrappedObject.getClass() : null); } /** @@ -303,7 +303,7 @@ protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) th catch (NotReadablePropertyException ex) { throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value in property referenced " + - "in indexed property path '" + propertyName + "'", ex); + "in indexed property path '" + propertyName + "'", ex); } // Set value for last key. String key = tokens.keys[tokens.keys.length - 1]; @@ -318,7 +318,7 @@ protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) th else { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value in property referenced " + - "in indexed property path '" + propertyName + "': returned null"); + "in indexed property path '" + propertyName + "': returned null"); } } if (propValue.getClass().isArray()) { @@ -367,8 +367,8 @@ else if (propValue instanceof List) { catch (NullPointerException ex) { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Cannot set element with index " + index + " in List of size " + - size + ", accessed using property path '" + propertyName + - "': List does not support filling up gaps with null elements"); + size + ", accessed using property path '" + propertyName + + "': List does not support filling up gaps with null elements"); } } list.add(convertedValue); @@ -405,7 +405,7 @@ else if (propValue instanceof Map) { else { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Property referenced in indexed property path '" + propertyName + - "' is neither an array nor a List nor a Map; returned value was [" + propValue + "]"); + "' is neither an array nor a List nor a Map; returned value was [" + propValue + "]"); } } @@ -451,7 +451,7 @@ else if (propValue instanceof Map) { } pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue); } - ph.setValue(object, valueToApply); + ph.setValue(this.wrappedObject, valueToApply); } catch (TypeMismatchException ex) { throw ex; @@ -800,7 +800,7 @@ protected String getFinalPath(AbstractNestablePropertyAccessor pa, String nested /** * Recursively navigate to return a property accessor for the nested property path. - * @param propertyPath property property path, which may be nested + * @param propertyPath property path, which may be nested * @return a property accessor for the target bean */ @SuppressWarnings("unchecked") // avoid nested generic @@ -914,9 +914,7 @@ else if (Map.class.isAssignableFrom(type)) { return BeanUtils.instantiate(type); } } - catch (Exception ex) { - // TODO: Root cause exception context is lost here; just exception message preserved. - // Should we throw another exception type that preserves context instead? + catch (Throwable ex) { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + name, "Could not instantiate property type [" + type.getName() + "] to auto-grow nested property path: " + ex); } @@ -942,7 +940,8 @@ private PropertyTokenHolder getPropertyNameTokens(String propertyName) { actualName = propertyName.substring(0, keyStart); } String key = propertyName.substring(keyStart + PROPERTY_KEY_PREFIX.length(), keyEnd); - if ((key.startsWith("'") && key.endsWith("'")) || (key.startsWith("\"") && key.endsWith("\""))) { + if (key.length() > 1 && (key.startsWith("'") && key.endsWith("'")) || + (key.startsWith("\"") && key.endsWith("\""))) { key = key.substring(1, key.length() - 1); } keys.add(key); @@ -953,10 +952,9 @@ private PropertyTokenHolder getPropertyNameTokens(String propertyName) { tokens.actualName = (actualName != null ? actualName : propertyName); tokens.canonicalName = tokens.actualName; if (!keys.isEmpty()) { - tokens.canonicalName += - PROPERTY_KEY_PREFIX + - StringUtils.collectionToDelimitedString(keys, PROPERTY_KEY_SUFFIX + PROPERTY_KEY_PREFIX) + - PROPERTY_KEY_SUFFIX; + tokens.canonicalName += PROPERTY_KEY_PREFIX + + StringUtils.collectionToDelimitedString(keys, PROPERTY_KEY_SUFFIX + PROPERTY_KEY_PREFIX) + + PROPERTY_KEY_SUFFIX; tokens.keys = StringUtils.toStringArray(keys); } return tokens; @@ -965,8 +963,8 @@ private PropertyTokenHolder getPropertyNameTokens(String propertyName) { @Override public String toString() { StringBuilder sb = new StringBuilder(getClass().getName()); - if (this.object != null) { - sb.append(": wrapping object [").append(ObjectUtils.identityToString(this.object)).append("]"); + if (this.wrappedObject != null) { + sb.append(": wrapping object [").append(ObjectUtils.identityToString(this.wrappedObject)).append("]"); } else { sb.append(": no wrapped object set"); 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 078712179a..216e5a4aab 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java @@ -148,7 +148,7 @@ public Class getPropertyType(String propertyPath) { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyAccessException if the property was valid but the - * accessor method failed or a type mismatch occured + * accessor method failed or a type mismatch occurred */ @Override public abstract void setPropertyValue(String propertyName, Object value) throws BeansException; 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 1b3993476e..5c735f16fb 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -278,8 +278,12 @@ public static Method findMethodWithMinimalParameters(Method[] methods, String me targetMethod = method; numMethodsFoundWithCurrentMinimumArgs = 1; } - else { - if (targetMethod.getParameterTypes().length == numParams) { + else if (!method.isBridge() && targetMethod.getParameterTypes().length == numParams) { + if (targetMethod.isBridge()) { + // Prefer regular method over bridge... + targetMethod = method; + } + else { // Additional candidate with same length numMethodsFoundWithCurrentMinimumArgs++; } @@ -289,7 +293,7 @@ public static Method findMethodWithMinimalParameters(Method[] methods, String me if (numMethodsFoundWithCurrentMinimumArgs > 1) { throw new IllegalArgumentException("Cannot resolve method '" + methodName + "' to a unique method. Attempted to resolve to overloaded method with " + - "the least number of parameters, but there were " + + "the least number of parameters but there were " + numMethodsFoundWithCurrentMinimumArgs + " candidates."); } return targetMethod; diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanWrapper.java b/spring-beans/src/main/java/org/springframework/beans/BeanWrapper.java index 7131651c6e..437a450c61 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanWrapper.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,6 +48,19 @@ */ public interface BeanWrapper extends ConfigurablePropertyAccessor { + /** + * Specify a limit for array and collection auto-growing. + *

Default is unlimited on a plain BeanWrapper. + * @since 4.1 + */ + void setAutoGrowCollectionLimit(int autoGrowCollectionLimit); + + /** + * Return the limit for array and collection auto-growing. + * @since 4.1 + */ + int getAutoGrowCollectionLimit(); + /** * Return the bean instance wrapped by this object, if any. * @return the bean instance, or {@code null} if none set @@ -78,15 +91,4 @@ public interface BeanWrapper extends ConfigurablePropertyAccessor { */ PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException; - /** - * Specify a limit for array and collection auto-growing. - *

Default is unlimited on a plain BeanWrapper. - */ - void setAutoGrowCollectionLimit(int autoGrowCollectionLimit); - - /** - * Return the limit for array and collection auto-growing. - */ - int getAutoGrowCollectionLimit(); - } 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 6625cc1aa4..6615661d4b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -136,23 +136,7 @@ private BeanWrapperImpl(Object object, String nestedPath, BeanWrapperImpl parent @Override public void setWrappedInstance(Object object, String nestedPath, Object rootObject) { super.setWrappedInstance(object, nestedPath, rootObject); - setIntrospectionClass(getWrappedInstance().getClass()); - } - - /** - * Set the security context used during the invocation of the wrapped instance methods. - * Can be null. - */ - public void setSecurityContext(AccessControlContext acc) { - this.acc = acc; - } - - /** - * Return the security context used during the invocation of the wrapped instance methods. - * Can be null. - */ - public AccessControlContext getSecurityContext() { - return this.acc; + setIntrospectionClass(getWrappedClass()); } /** @@ -161,8 +145,7 @@ public AccessControlContext getSecurityContext() { * @param clazz the class to introspect */ protected void setIntrospectionClass(Class clazz) { - if (this.cachedIntrospectionResults != null && - !clazz.equals(this.cachedIntrospectionResults.getBeanClass())) { + if (this.cachedIntrospectionResults != null && this.cachedIntrospectionResults.getBeanClass() != clazz) { this.cachedIntrospectionResults = null; } } @@ -179,6 +162,22 @@ private CachedIntrospectionResults getCachedIntrospectionResults() { return this.cachedIntrospectionResults; } + /** + * Set the security context used during the invocation of the wrapped instance methods. + * Can be null. + */ + public void setSecurityContext(AccessControlContext acc) { + this.acc = acc; + } + + /** + * Return the security context used during the invocation of the wrapped instance methods. + * Can be null. + */ + public AccessControlContext getSecurityContext() { + return this.acc; + } + /** * Convert the given value for the specified property to the latter's type. 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 00068ea4b6..c7faf35524 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-2012 the original author or authors. + * Copyright 2002-2016 the original author 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 interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyAccessException if the property was valid but the - * accessor method failed or a type mismatch occured + * accessor method failed or a type mismatch occurred */ void setPropertyValue(String propertyName, Object value) throws BeansException; @@ -130,7 +130,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyAccessException if the property was valid but the - * accessor method failed or a type mismatch occured + * accessor method failed or a type mismatch occurred */ void setPropertyValue(PropertyValue pv) throws BeansException; @@ -144,7 +144,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. */ @@ -164,7 +164,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. * @see #setPropertyValues(PropertyValues, boolean, boolean) @@ -185,7 +185,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. * @see #setPropertyValues(PropertyValues, boolean, boolean) @@ -208,7 +208,7 @@ void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown) * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. */ 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 87cea92830..87ccf2a95c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyMatches.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyMatches.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author 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,8 +30,8 @@ * Helper class for calculating property matches, according to a configurable * distance. Provide the list of potential matches and an easy way to generate * an error message. Works for both java bean properties and fields. - *

- * Mainly for use within the framework and in particular the binding facility + * + *

Mainly for use within the framework and in particular the binding facility. * * @author Alef Arendsen * @author Arjen Poutsma @@ -43,14 +43,12 @@ */ public abstract class PropertyMatches { - //--------------------------------------------------------------------- - // Static section - //--------------------------------------------------------------------- - /** Default maximum property distance: 2 */ public static final int DEFAULT_MAX_DISTANCE = 2; + // Static factory methods + /** * Create PropertyMatches for the given bean property. * @param propertyName the name of the property to find possible matches for @@ -90,9 +88,7 @@ public static PropertyMatches forField(String propertyName, Class beanClass, } - //--------------------------------------------------------------------- - // Instance section - //--------------------------------------------------------------------- + // Instance state private final String propertyName; @@ -107,18 +103,19 @@ private PropertyMatches(String propertyName, String[] possibleMatches) { this.possibleMatches = possibleMatches; } + /** * Return the name of the requested property. */ public String getPropertyName() { - return propertyName; + return this.propertyName; } /** * Return the calculated possible matches. */ public String[] getPossibleMatches() { - return possibleMatches; + return this.possibleMatches; } /** @@ -127,6 +124,9 @@ public String[] getPossibleMatches() { */ public abstract String buildErrorMessage(); + + // Implementation support for subclasses + protected void appendHintMessage(StringBuilder msg) { msg.append("Did you mean "); for (int i = 0; i < this.possibleMatches.length; i++) { @@ -184,9 +184,12 @@ private static int calculateStringDistance(String s1, String s2) { return d[s1.length()][s2.length()]; } + + // Concrete subclasses + private static class BeanPropertyMatches extends PropertyMatches { - private BeanPropertyMatches(String propertyName, Class beanClass, int maxDistance) { + public BeanPropertyMatches(String propertyName, Class beanClass, int maxDistance) { super(propertyName, calculateMatches(propertyName, BeanUtils.getPropertyDescriptors(beanClass), maxDistance)); } @@ -231,12 +234,12 @@ public String buildErrorMessage() { } return msg.toString(); } - } + private static class FieldPropertyMatches extends PropertyMatches { - private FieldPropertyMatches(String propertyName, Class beanClass, int maxDistance) { + public FieldPropertyMatches(String propertyName, Class beanClass, int maxDistance) { super(propertyName, calculateMatches(propertyName, beanClass, maxDistance)); } @@ -255,7 +258,6 @@ public void doWith(Field field) throws IllegalArgumentException, IllegalAccessEx return StringUtils.toStringArray(candidates); } - @Override public String buildErrorMessage() { String propertyName = getPropertyName(); @@ -270,7 +272,6 @@ public String buildErrorMessage() { } return msg.toString(); } - } } 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 4f4f37ca79..3c2f4e6c5b 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,8 +45,6 @@ public class PropertyValue extends BeanMetadataAttributeAccessor implements Seri private final Object value; - private Object source; - private boolean optional = false; private boolean converted = false; @@ -78,12 +76,12 @@ public PropertyValue(PropertyValue original) { Assert.notNull(original, "Original must not be null"); this.name = original.getName(); this.value = original.getValue(); - this.source = original.getSource(); this.optional = original.isOptional(); this.converted = original.converted; this.convertedValue = original.convertedValue; this.conversionNecessary = original.conversionNecessary; this.resolvedTokens = original.resolvedTokens; + setSource(original.getSource()); copyAttributesFrom(original); } @@ -97,10 +95,10 @@ public PropertyValue(PropertyValue original, Object newValue) { Assert.notNull(original, "Original must not be null"); this.name = original.getName(); this.value = newValue; - this.source = original; this.optional = original.isOptional(); this.conversionNecessary = original.conversionNecessary; this.resolvedTokens = original.resolvedTokens; + setSource(original); copyAttributesFrom(original); } @@ -129,16 +127,28 @@ public Object getValue() { */ public PropertyValue getOriginalPropertyValue() { PropertyValue original = this; - while (original.source instanceof PropertyValue && original.source != original) { - original = (PropertyValue) original.source; + Object source = getSource(); + while (source instanceof PropertyValue && source != original) { + original = (PropertyValue) source; + source = original.getSource(); } return original; } + /** + * Set whether this is an optional value, that is, to be ignored + * when no corresponding property exists on the target class. + * @since 3.0 + */ public void setOptional(boolean optional) { this.optional = optional; } + /** + * Return whether this is an optional value, that is, to be ignored + * when no corresponding property exists on the target class. + * @since 3.0 + */ public boolean isOptional() { return this.optional; } @@ -180,7 +190,7 @@ public boolean equals(Object other) { PropertyValue otherPv = (PropertyValue) other; return (this.name.equals(otherPv.name) && ObjectUtils.nullSafeEquals(this.value, otherPv.value) && - ObjectUtils.nullSafeEquals(this.source, otherPv.source)); + ObjectUtils.nullSafeEquals(getSource(), otherPv.getSource())); } @Override 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 9b1eda75ee..f55bc5f1c1 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -183,9 +183,10 @@ public T convertIfNecessary(String propertyName, Object oldValue, Object new // Value not of required type? if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) { - if (requiredType != null && Collection.class.isAssignableFrom(requiredType) && convertedValue instanceof String) { - TypeDescriptor elementType = typeDescriptor.getElementTypeDescriptor(); - if (elementType != null && Enum.class.isAssignableFrom(elementType.getType())) { + if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) && + convertedValue instanceof String) { + TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor(); + if (elementTypeDesc != null && Enum.class.isAssignableFrom(elementTypeDesc.getType())) { convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue); } } @@ -265,7 +266,7 @@ else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requi } else { // convertedValue == null - if (javaUtilOptionalEmpty != null && requiredType.equals(javaUtilOptionalEmpty.getClass())) { + if (javaUtilOptionalEmpty != null && requiredType == javaUtilOptionalEmpty.getClass()) { convertedValue = javaUtilOptionalEmpty; } } 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 ec1f5a2b4c..9be206c694 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-2012 the original author or authors. + * Copyright 2002-2016 the original author 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-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java index f0c2fa1dae..7af4087576 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 @@ -123,7 +123,7 @@ public String getResourceDescription() { /** * Add a related cause to this bean creation exception, - * not being a direct cause of the failure but having occured + * not being a direct cause of the failure but having occurred * earlier in the creation of the same bean instance. * @param ex the related cause to add */ 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 3c6bd4c8b6..ba0d0b9db9 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,24 +67,27 @@ * 1. BeanNameAware's {@code setBeanName}
* 2. BeanClassLoaderAware's {@code setBeanClassLoader}
* 3. BeanFactoryAware's {@code setBeanFactory}
- * 4. ResourceLoaderAware's {@code setResourceLoader} + * 4. EnvironmentAware's {@code setEnvironment} + * 5. EmbeddedValueResolverAware's {@code setEmbeddedValueResolver} + * 6. ResourceLoaderAware's {@code setResourceLoader} * (only applicable when running in an application context)
- * 5. ApplicationEventPublisherAware's {@code setApplicationEventPublisher} + * 7. ApplicationEventPublisherAware's {@code setApplicationEventPublisher} * (only applicable when running in an application context)
- * 6. MessageSourceAware's {@code setMessageSource} + * 8. MessageSourceAware's {@code setMessageSource} * (only applicable when running in an application context)
- * 7. ApplicationContextAware's {@code setApplicationContext} + * 9. ApplicationContextAware's {@code setApplicationContext} * (only applicable when running in an application context)
- * 8. ServletContextAware's {@code setServletContext} + * 10. ServletContextAware's {@code setServletContext} * (only applicable when running in a web application context)
- * 9. {@code postProcessBeforeInitialization} methods of BeanPostProcessors
- * 10. InitializingBean's {@code afterPropertiesSet}
- * 11. a custom init-method definition
- * 12. {@code postProcessAfterInitialization} methods of BeanPostProcessors + * 11. {@code postProcessBeforeInitialization} methods of BeanPostProcessors
+ * 12. InitializingBean's {@code afterPropertiesSet}
+ * 13. a custom init-method definition
+ * 14. {@code postProcessAfterInitialization} methods of BeanPostProcessors * *

On shutdown of a bean factory, the following lifecycle methods apply:
- * 1. DisposableBean's {@code destroy}
- * 2. a custom destroy-method definition + * 1. {@code postProcessBeforeDestruction} methods of DestructionAwareBeanPostProcessors + * 2. DisposableBean's {@code destroy}
+ * 3. a custom destroy-method definition * * @author Rod Johnson * @author Juergen Hoeller @@ -153,12 +156,12 @@ public interface BeanFactory { /** * Return the bean instance that uniquely matches the given object type, if any. - * @param requiredType type the bean must match; can be an interface or superclass. - * {@code null} is disallowed. *

This method goes into {@link ListableBeanFactory} by-type lookup territory * 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. * @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 @@ -188,12 +191,12 @@ public interface BeanFactory { * Return an instance, which may be shared or independent, of the specified bean. *

Allows for specifying explicit constructor arguments / factory method arguments, * overriding the specified default arguments (if any) in the bean definition. - * @param requiredType type the bean must match; can be an interface or superclass. - * {@code null} is disallowed. *

This method goes into {@link ListableBeanFactory} by-type lookup territory * 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 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 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 e9bc88c84f..e8be332289 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -109,6 +109,7 @@ public interface ListableBeanFactory extends BeanFactory { * @return the names of beans (or objects created by FactoryBeans) matching * the given object type (including subclasses), or an empty array if none * @since 4.2 + * @see #isTypeMatch(String, ResolvableType) * @see FactoryBean#getObjectType * @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, ResolvableType) */ diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/NamedBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/NamedBean.java index e1e5ed33f5..9f2453ff65 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/NamedBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/NamedBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2016 the original author 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,10 +17,10 @@ package org.springframework.beans.factory; /** - * Counterpart of BeanNameAware. Returns the bean name of an object. + * Counterpart of {@link BeanNameAware}. Returns the bean name of an object. * - *

This interface can be introduced to avoid a brittle dependence - * on bean name in objects used with Spring IoC and Spring AOP. + *

This interface can be introduced to avoid a brittle dependence on + * bean name in objects used with Spring IoC and Spring AOP. * * @author Rod Johnson * @since 2.0 @@ -29,7 +29,7 @@ public interface NamedBean { /** - * Return the name of this bean in a Spring bean factory. + * Return the name of this bean in a Spring bean factory, if known. */ String getBeanName(); 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 f539e70180..9ec35a7e63 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-2012 the original author or authors. + * Copyright 2002-2016 the original author 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,7 +56,7 @@ public UnsatisfiedDependencyException( public UnsatisfiedDependencyException( String resourceDescription, String beanName, String propertyName, BeansException ex) { - this(resourceDescription, beanName, propertyName, (ex != null ? ": " + ex.getMessage() : "")); + this(resourceDescription, beanName, propertyName, (ex != null ? ex.getMessage() : "")); initCause(ex); } @@ -88,7 +88,7 @@ public UnsatisfiedDependencyException( public UnsatisfiedDependencyException( String resourceDescription, String beanName, int ctorArgIndex, Class ctorArgType, BeansException ex) { - this(resourceDescription, beanName, ctorArgIndex, ctorArgType, (ex != null ? ": " + ex.getMessage() : "")); + this(resourceDescription, beanName, ctorArgIndex, ctorArgType, (ex != null ? ex.getMessage() : "")); initCause(ex); } 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 ebc3cbf53c..0d7eb6ee3b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -141,8 +141,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) throws BeansException { if (!this.validatedBeanNames.contains(beanName)) { if (!shouldSkip(this.beanFactory, beanName)) { 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 bc8a42f71b..85faab006b 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-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -208,7 +208,7 @@ public void destroy() throws Exception { *

Invoked on initialization of this FactoryBean in case of * a singleton; else, on each {@link #getObject()} call. * @return the object returned by this factory - * @throws Exception if an exception occured during object creation + * @throws Exception if an exception occurred during object creation * @see #getObject() */ protected abstract T createInstance() throws Exception; 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 ffa8849a25..ef7735792b 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-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,8 @@ import org.springframework.beans.BeansException; import org.springframework.beans.TypeConverter; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; /** * Extension of the {@link org.springframework.beans.factory.BeanFactory} @@ -114,9 +116,9 @@ public interface AutowireCapableBeanFactory extends BeanFactory { *

Performs full initialization of the bean, including all applicable * {@link BeanPostProcessor BeanPostProcessors}. *

Note: This is intended for creating a fresh instance, populating annotated - * fields and methods as well as applying all standard bean initialiation callbacks. + * 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; - * use {@link #createBean(Class, int, boolean)} for that purposes. + * use {@link #createBean(Class, int, boolean)} for those purposes. * @param beanClass the class of the bean to create * @return the new bean instance * @throws BeansException if instantiation or wiring failed @@ -129,7 +131,7 @@ public interface AutowireCapableBeanFactory extends BeanFactory { *

Note: This is essentially intended for (re-)populating annotated fields and * methods, either for new instances or for deserialized instances. It does * not imply traditional by-name or by-type autowiring of properties; - * use {@link #autowireBeanProperties} for that purposes. + * use {@link #autowireBeanProperties} for those purposes. * @param existingBean the existing bean instance * @throws BeansException if wiring failed */ @@ -154,15 +156,6 @@ public interface AutowireCapableBeanFactory extends BeanFactory { */ Object configureBean(Object existingBean, String beanName) throws BeansException; - /** - * Resolve the specified dependency against the beans defined in this factory. - * @param descriptor the descriptor for the dependency - * @param beanName the name of the bean which declares the present dependency - * @return the resolved object, or {@code null} if none found - * @throws BeansException in dependency resolution failed - */ - Object resolveDependency(DependencyDescriptor descriptor, String beanName) throws BeansException; - //------------------------------------------------------------------------- // Specialized methods for fine-grained control over the bean lifecycle @@ -312,18 +305,39 @@ Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String be */ void destroyBean(Object existingBean); + + //------------------------------------------------------------------------- + // Delegate methods for resolving injection points + //------------------------------------------------------------------------- + + /** + * Resolve the specified dependency against the beans defined in this factory. + * @param descriptor the descriptor for the dependency (field/method/constructor) + * @param requestingBeanName the name of the bean which declares the given dependency + * @return the resolved object, or {@code null} if none found + * @throws NoSuchBeanDefinitionException if no matching bean was found + * @throws NoUniqueBeanDefinitionException if more than one matching bean was found + * @throws BeansException if dependency resolution failed for any other reason + * @since 2.5 + * @see #resolveDependency(DependencyDescriptor, String, Set, TypeConverter) + */ + Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName) throws BeansException; + /** * Resolve the specified dependency against the beans defined in this factory. - * @param descriptor the descriptor for the dependency - * @param beanName the name of the bean which declares the present dependency + * @param descriptor the descriptor for the dependency (field/method/constructor) + * @param requestingBeanName the name of the bean which declares the given dependency * @param autowiredBeanNames a Set that all names of autowired beans (used for - * resolving the present dependency) are supposed to be added to - * @param typeConverter the TypeConverter to use for populating arrays and - * collections + * resolving the given dependency) are supposed to be added to + * @param typeConverter the TypeConverter to use for populating arrays and collections * @return the resolved object, or {@code null} if none found - * @throws BeansException in dependency resolution failed + * @throws NoSuchBeanDefinitionException if no matching bean was found + * @throws NoUniqueBeanDefinitionException if more than one matching bean was found + * @throws BeansException if dependency resolution failed for any other reason + * @since 2.5 + * @see DependencyDescriptor */ - Object resolveDependency(DependencyDescriptor descriptor, String beanName, + Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException; } 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 fd38f364cc..88642eb493 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-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -196,6 +196,7 @@ public void increaseNestingLevel() { * Optionally set the concrete class that contains this dependency. * This may differ from the class that declares the parameter/field in that * it may be a subclass thereof, potentially substituting type variables. + * @since 4.0 */ public void setContainingClass(Class containingClass) { this.containingClass = containingClass; @@ -206,6 +207,7 @@ public void setContainingClass(Class containingClass) { /** * Build a ResolvableType object for the wrapped parameter/field. + * @since 4.0 */ public ResolvableType getResolvableType() { return (this.field != null ? ResolvableType.forField(this.field, this.nestingLevel, this.containingClass) : @@ -217,6 +219,7 @@ public ResolvableType getResolvableType() { *

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} * that a fallback match is acceptable as well. + * @since 4.0 */ public boolean fallbackMatchAllowed() { return false; @@ -224,6 +227,7 @@ public boolean fallbackMatchAllowed() { /** * Return a variant of this descriptor that is intended for a fallback match. + * @since 4.0 * @see #fallbackMatchAllowed() */ public DependencyDescriptor forFallbackMatch() { 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 aca839616c..6f035c1732 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-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -204,7 +204,7 @@ public Object getObject() throws IllegalAccessException { // instance field return this.fieldObject.get(this.targetObject); } - else{ + else { // class field return this.fieldObject.get(null); } 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 f508e3fff1..0186221b58 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-2012 the original author or authors. + * Copyright 2002-2016 the original author 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,14 +96,13 @@ public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { * 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 to the given 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 * @throws org.springframework.beans.BeansException in case of errors * @see org.springframework.beans.MutablePropertyValues */ PropertyValues postProcessPropertyValues( - PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) - throws BeansException; + 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 3e02846fcb..3f7e1746b7 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-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,8 +66,7 @@ public boolean postProcessAfterInstantiation(Object bean, String beanName) throw @Override public PropertyValues postProcessPropertyValues( - PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) - throws BeansException { + PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { return pvs; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java index 93a97a8dcf..adc415d0d5 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -95,7 +95,7 @@ public Class getObjectType() { *

Invoked on initialization of this FactoryBean in case of a * shared singleton; else, on each {@link #getObject()} call. * @return the object returned by this factory - * @throws IOException if an exception occured during properties loading + * @throws IOException if an exception occurred during properties loading * @see #mergeProperties() */ protected Properties createProperties() throws IOException { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java index 8777fa61e0..4e313c7919 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author 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,6 +68,7 @@ public interface Scope { * @param objectFactory the {@link ObjectFactory} to use to create the scoped * object if it is not present in the underlying storage mechanism * @return the desired object (never {@code null}) + * @throws IllegalStateException if the underlying scope is not currently active */ Object get(String name, ObjectFactory objectFactory); @@ -84,6 +85,7 @@ public interface Scope { * removing an object. * @param name the name of the object to remove * @return the removed object, or {@code null} if no object was present + * @throws IllegalStateException if the underlying scope is not currently active * @see #registerDestructionCallback */ Object remove(String name); @@ -100,7 +102,7 @@ public interface Scope { * at the appropriate time. If such a callback is not supported by the * underlying runtime environment at all, the callback must be * ignored and a corresponding warning should be logged. - *

Note that 'destruction' refers to to automatic destruction of + *

Note that 'destruction' refers to automatic destruction of * the object as part of the scope's own lifecycle, not to the individual * scoped object having been explicitly removed by the application. * If a scoped object gets removed via this facade's {@link #remove(String)} @@ -112,6 +114,7 @@ public interface Scope { * so it can safely be executed without an enclosing try-catch block. * Furthermore, the Runnable will usually be serializable, provided * that its target object is serializable as well. + * @throws IllegalStateException if the underlying scope is not currently active * @see org.springframework.beans.factory.DisposableBean * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getDestroyMethodName() * @see DestructionAwareBeanPostProcessor @@ -123,6 +126,7 @@ public interface Scope { * E.g. the HttpServletRequest object for key "request". * @param key the contextual key * @return the corresponding object, or {@code null} if none found + * @throws IllegalStateException if the underlying scope is not currently active */ Object resolveContextualObject(String key); @@ -139,6 +143,7 @@ public interface Scope { * underlying storage mechanism has no obvious candidate for such an ID. * @return the conversation ID, or {@code null} if there is no * conversation ID for the current scope + * @throws IllegalStateException if the underlying scope is not currently active */ String getConversationId(); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index cf25cd5b9a..1273d61f4c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author 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,8 +325,8 @@ public Object configureBean(Object existingBean, String beanName) throws BeansEx } @Override - public Object resolveDependency(DependencyDescriptor descriptor, String beanName) throws BeansException { - return resolveDependency(descriptor, beanName, null, null); + public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName) throws BeansException { + return resolveDependency(descriptor, requestingBeanName, null, null); } @@ -500,7 +500,9 @@ protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] ar * @see #instantiateUsingFactoryMethod * @see #autowireConstructor */ - protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { + protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) + throws BeanCreationException { + // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { @@ -515,7 +517,13 @@ protected Object doCreateBean(final String beanName, final RootBeanDefinition mb // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { - applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); + try { + applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); + } + catch (Throwable ex) { + throw new BeanCreationException(mbd.getResourceDescription(), beanName, + "Post-processing of merged bean definition failed", ex); + } mbd.postProcessed = true; } } @@ -550,7 +558,8 @@ public Object getObject() throws BeansException { throw (BeanCreationException) ex; } else { - throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); + throw new BeanCreationException( + mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } @@ -586,7 +595,8 @@ else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { - throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); + throw new BeanCreationException( + mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; @@ -624,7 +634,8 @@ protected Class predictBeanType(String beanName, RootBeanDefinition mbd, Clas protected Class determineTargetType(String beanName, RootBeanDefinition mbd, Class... typesToMatch) { Class targetType = mbd.getTargetType(); if (targetType == null) { - targetType = (mbd.getFactoryMethodName() != null ? getTypeForFactoryMethod(beanName, mbd, typesToMatch) : + targetType = (mbd.getFactoryMethodName() != null ? + getTypeForFactoryMethod(beanName, mbd, typesToMatch) : resolveBeanClass(mbd, beanName, typesToMatch)); if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) { mbd.setTargetType(targetType); @@ -772,10 +783,11 @@ class Holder { Class value = null; } ReflectionUtils.doWithMethods(fbClass, new ReflectionUtils.MethodCallback() { @Override - public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { + public void doWith(Method method) { if (method.getName().equals(factoryMethodName) && FactoryBean.class.isAssignableFrom(method.getReturnType())) { - objectType.value = GenericTypeResolver.resolveReturnTypeArgument(method, FactoryBean.class); + objectType.value = GenericTypeResolver.resolveReturnTypeArgument( + method, FactoryBean.class); } } }); @@ -802,10 +814,14 @@ public void doWith(Method method) throws IllegalArgumentException, IllegalAccess if (objectType.value != null) { return objectType.value; } + else { + // No type found for shortcut FactoryBean instance: + // fall back to full creation of the FactoryBean instance. + return super.getTypeForFactoryBean(beanName, mbd); + } } - // No type found - fall back to full creation of the FactoryBean instance. - return super.getTypeForFactoryBean(beanName, mbd); + return null; } /** @@ -824,7 +840,7 @@ protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); if (exposedObject == null) { - return exposedObject; + return null; } } } @@ -922,24 +938,15 @@ private FactoryBean getNonSingletonFactoryBeanForTypeCheck(String beanName, R * @param mbd the merged bean definition for the bean * @param beanType the actual type of the managed bean instance * @param beanName the name of the bean - * @throws BeansException if any post-processing failed * @see MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition */ - protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class beanType, String beanName) - throws BeansException { - - try { - for (BeanPostProcessor bp : getBeanPostProcessors()) { - if (bp instanceof MergedBeanDefinitionPostProcessor) { - MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp; - bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName); - } + protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class beanType, String beanName) { + for (BeanPostProcessor bp : getBeanPostProcessors()) { + if (bp instanceof MergedBeanDefinitionPostProcessor) { + MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp; + bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName); } } - catch (Exception ex) { - throw new BeanCreationException(mbd.getResourceDescription(), beanName, - "Post-processing failed of bean type [" + beanType + "] failed", ex); - } } /** @@ -976,12 +983,9 @@ protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition * @param beanClass the class of the bean to be instantiated * @param beanName the name of the bean * @return the bean object to use instead of a default instance of the target bean, or {@code null} - * @throws BeansException if any post-processing failed * @see InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation */ - protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName) - throws BeansException { - + protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; @@ -1102,7 +1106,8 @@ public Object run() { return bw; } catch (Throwable ex) { - throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); + throw new BeanCreationException( + mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } } @@ -1654,7 +1659,9 @@ public Object run() throws Exception { * methods with arguments. * @see #invokeInitMethods */ - protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable { + protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd) + throws Throwable { + String initMethodName = mbd.getInitMethodName(); final Method initMethod = (mbd.isNonPublicAccessAllowed() ? BeanUtils.findMethod(bean.getClass(), initMethodName) : diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java index 85b5de4a50..a0d7dc21dd 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -731,7 +731,7 @@ public void setMethodOverrides(MethodOverrides methodOverrides) { /** * Return information about methods to be overridden by the IoC * container. This will be empty if there are no method overrides. - * Never returns null. + * Never returns {@code null}. */ public MethodOverrides getMethodOverrides() { return this.methodOverrides; @@ -934,8 +934,11 @@ public void prepareMethodOverrides() throws BeanDefinitionValidationException { // Check that lookup methods exists. MethodOverrides methodOverrides = getMethodOverrides(); if (!methodOverrides.isEmpty()) { - for (MethodOverride mo : methodOverrides.getOverrides()) { - prepareMethodOverride(mo); + Set overrides = methodOverrides.getOverrides(); + synchronized (overrides) { + for (MethodOverride mo : overrides) { + prepareMethodOverride(mo); + } } } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index cddecfc3b1..cc6c614939 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -287,13 +287,13 @@ protected T doGetBean( // Guarantee initialization of beans that the current bean depends on. String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { - for (String dependsOnBean : dependsOn) { - if (isDependent(beanName, dependsOnBean)) { + for (String dep : dependsOn) { + if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, - "Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'"); + "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } - registerDependentBean(dependsOnBean, beanName); - getBean(dependsOnBean); + registerDependentBean(dep, beanName); + getBean(dep); } } @@ -460,19 +460,19 @@ public boolean isPrototype(String name) throws NoSuchBeanDefinitionException { return false; } if (isFactoryBean(beanName, mbd)) { - final FactoryBean factoryBean = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName); + final FactoryBean fb = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName); if (System.getSecurityManager() != null) { return AccessController.doPrivileged(new PrivilegedAction() { @Override public Boolean run() { - return ((factoryBean instanceof SmartFactoryBean && ((SmartFactoryBean) factoryBean).isPrototype()) || - !factoryBean.isSingleton()); + return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean) fb).isPrototype()) || + !fb.isSingleton()); } }, getAccessControlContext()); } else { - return ((factoryBean instanceof SmartFactoryBean && ((SmartFactoryBean) factoryBean).isPrototype()) || - !factoryBean.isSingleton()); + return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean) fb).isPrototype()) || + !fb.isSingleton()); } } else { @@ -800,12 +800,15 @@ public void addEmbeddedValueResolver(StringValueResolver valueResolver) { @Override public String resolveEmbeddedValue(String value) { + if (value == null) { + return null; + } String result = value; for (StringValueResolver resolver : this.embeddedValueResolvers) { + result = resolver.resolveStringValue(result); if (result == null) { return null; } - result = resolver.resolveStringValue(result); } return result; } @@ -1045,11 +1048,11 @@ public void destroyBean(String beanName, Object beanInstance) { * Destroy the given bean instance (usually a prototype instance * obtained from this factory) according to the given bean definition. * @param beanName the name of the bean definition - * @param beanInstance the bean instance to destroy + * @param bean the bean instance to destroy * @param mbd the merged bean definition */ - protected void destroyBean(String beanName, Object beanInstance, RootBeanDefinition mbd) { - new DisposableBeanAdapter(beanInstance, beanName, mbd, getBeanPostProcessors(), getAccessControlContext()).destroy(); + protected void destroyBean(String beanName, Object bean, RootBeanDefinition mbd) { + new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), getAccessControlContext()).destroy(); } @Override @@ -1230,12 +1233,13 @@ protected RootBeanDefinition getMergedBeanDefinition( pbd = getMergedBeanDefinition(parentBeanName); } else { - if (getParentBeanFactory() instanceof ConfigurableBeanFactory) { - pbd = ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(parentBeanName); + BeanFactory parent = getParentBeanFactory(); + if (parent instanceof ConfigurableBeanFactory) { + pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName); } else { - throw new NoSuchBeanDefinitionException(bd.getParentName(), - "Parent name '" + bd.getParentName() + "' is equal to bean name '" + beanName + + throw new NoSuchBeanDefinitionException(parentBeanName, + "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName + "': cannot be resolved without an AbstractBeanFactory parent"); } } @@ -1351,12 +1355,14 @@ public Class run() throws Exception { catch (ClassNotFoundException ex) { throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex); } - catch (LinkageError err) { - throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), err); + catch (LinkageError ex) { + throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex); } } - private Class doResolveBeanClass(RootBeanDefinition mbd, Class... typesToMatch) throws ClassNotFoundException { + private Class doResolveBeanClass(RootBeanDefinition mbd, Class... typesToMatch) + throws ClassNotFoundException { + ClassLoader beanClassLoader = getBeanClassLoader(); ClassLoader classLoaderToUse = beanClassLoader; if (!ObjectUtils.isEmpty(typesToMatch)) { @@ -1475,9 +1481,14 @@ protected Class getTypeForFactoryBean(String beanName, RootBeanDefinition mbd logger.debug("Bean currently in creation on FactoryBean type check: " + ex); } } + else if (mbd.isLazyInit()) { + if (logger.isDebugEnabled()) { + logger.debug("Bean creation exception on lazy FactoryBean type check: " + ex); + } + } else { if (logger.isWarnEnabled()) { - logger.warn("Bean creation exception on FactoryBean type check: " + ex); + logger.warn("Bean creation exception on non-lazy FactoryBean type check: " + ex); } } onSuppressedException(ex); @@ -1493,11 +1504,14 @@ protected Class getTypeForFactoryBean(String beanName, RootBeanDefinition mbd */ protected void markBeanAsCreated(String beanName) { if (!this.alreadyCreated.contains(beanName)) { - this.alreadyCreated.add(beanName); - - // Let the bean definition get re-merged now that we're actually creating - // the bean... just in case some of its metadata changed in the meantime. - clearMergedBeanDefinition(beanName); + synchronized (this.mergedBeanDefinitions) { + if (!this.alreadyCreated.contains(beanName)) { + // Let the bean definition get re-merged now that we're actually creating + // the bean... just in case some of its metadata changed in the meantime. + clearMergedBeanDefinition(beanName); + this.alreadyCreated.add(beanName); + } + } } } @@ -1506,7 +1520,9 @@ protected void markBeanAsCreated(String beanName) { * @param beanName the name of the bean */ protected void cleanupAfterBeanCreationFailure(String beanName) { - this.alreadyCreated.remove(beanName); + synchronized (this.mergedBeanDefinitions) { + this.alreadyCreated.remove(beanName); + } } /** diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java index ed6bc0661f..a1533835f9 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -166,6 +166,17 @@ public BeanDefinitionBuilder setFactoryMethod(String factoryMethod) { return this; } + /** + * Add an indexed constructor arg value. The current index is tracked internally + * and all additions are at the present point. + * @deprecated since Spring 2.5, in favor of {@link #addConstructorArgValue}. + * This variant just remains around for Spring Security 2.x compatibility. + */ + @Deprecated + public BeanDefinitionBuilder addConstructorArg(Object value) { + return addConstructorArgValue(value); + } + /** * Add an indexed constructor arg value. The current index is tracked internally * and all additions are at the present point. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java index f56f0e527a..8a745dab2c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,6 +69,23 @@ public static AbstractBeanDefinition createBeanDefinition( return bd; } + /** + * Generate a bean name for the given top-level bean definition, + * unique within the given bean factory. + * @param beanDefinition the bean definition to generate a bean name for + * @param registry the bean factory that the definition is going to be + * registered with (to check for existing bean names) + * @return the generated bean name + * @throws BeanDefinitionStoreException if no unique name can be generated + * for the given bean definition + * @see #generateBeanName(BeanDefinition, BeanDefinitionRegistry, boolean) + */ + public static String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) + throws BeanDefinitionStoreException { + + return generateBeanName(beanDefinition, registry, false); + } + /** * Generate a bean name for the given bean definition, unique within the * given bean factory. @@ -117,22 +134,6 @@ else if (definition.getFactoryBeanName() != null) { return id; } - /** - * Generate a bean name for the given top-level bean definition, - * unique within the given bean factory. - * @param beanDefinition the bean definition to generate a bean name for - * @param registry the bean factory that the definition is going to be - * registered with (to check for existing bean names) - * @return the generated bean name - * @throws BeanDefinitionStoreException if no unique name can be generated - * for the given bean definition - */ - public static String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) - throws BeanDefinitionStoreException { - - return generateBeanName(beanDefinition, registry, false); - } - /** * Register the given bean definition with the given bean factory. * @param definitionHolder the bean definition including name and aliases diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index 50c5679112..250b8cf95e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -151,7 +151,7 @@ public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefi catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Resolution of declared constructors on bean Class [" + beanClass.getName() + - "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex); + "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex); } } AutowireUtils.sortConstructors(candidates); @@ -609,8 +609,8 @@ public Object run() { private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw, ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) { - TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ? - this.beanFactory.getCustomTypeConverter() : bw); + TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); + TypeConverter converter = (customConverter != null ? customConverter : bw); BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter); @@ -666,8 +666,8 @@ private ArgumentsHolder createArgumentArray( boolean autowiring) throws UnsatisfiedDependencyException { String methodType = (methodOrCtor instanceof Constructor ? "constructor" : "factory method"); - TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ? - this.beanFactory.getCustomTypeConverter() : bw); + TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); + TypeConverter converter = (customConverter != null ? customConverter : bw); ArgumentsHolder args = new ArgumentsHolder(paramTypes.length); Set usedValueHolders = @@ -768,12 +768,13 @@ private ArgumentsHolder createArgumentArray( private Object[] resolvePreparedArguments( String beanName, RootBeanDefinition mbd, BeanWrapper bw, Member methodOrCtor, Object[] argsToResolve) { - Class[] paramTypes = (methodOrCtor instanceof Method ? - ((Method) methodOrCtor).getParameterTypes() : ((Constructor) methodOrCtor).getParameterTypes()); - TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ? - this.beanFactory.getCustomTypeConverter() : bw); + TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); + TypeConverter converter = (customConverter != null ? customConverter : bw); BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter); + Class[] paramTypes = (methodOrCtor instanceof Method ? + ((Method) methodOrCtor).getParameterTypes() : ((Constructor) methodOrCtor).getParameterTypes()); + Object[] resolvedArgs = new Object[argsToResolve.length]; for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) { Object argValue = argsToResolve[argIndex]; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 7a32060e28..350a89da3d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.LinkedHashMap; @@ -333,44 +332,47 @@ public T getBean(Class requiredType) throws BeansException { } @Override + @SuppressWarnings("unchecked") public T getBean(Class requiredType, Object... args) throws BeansException { Assert.notNull(requiredType, "Required type must not be null"); - String[] beanNames = getBeanNamesForType(requiredType); - if (beanNames.length > 1) { - ArrayList autowireCandidates = new ArrayList(); - for (String beanName : beanNames) { + String[] candidateNames = getBeanNamesForType(requiredType); + + if (candidateNames.length > 1) { + List autowireCandidates = new ArrayList(candidateNames.length); + for (String beanName : candidateNames) { if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) { autowireCandidates.add(beanName); } } if (autowireCandidates.size() > 0) { - beanNames = autowireCandidates.toArray(new String[autowireCandidates.size()]); + candidateNames = autowireCandidates.toArray(new String[autowireCandidates.size()]); } } - if (beanNames.length == 1) { - return getBean(beanNames[0], requiredType, args); + + if (candidateNames.length == 1) { + return getBean(candidateNames[0], requiredType, args); } - else if (beanNames.length > 1) { - Map candidates = new HashMap(); - for (String beanName : beanNames) { + else if (candidateNames.length > 1) { + Map candidates = new LinkedHashMap(); + for (String beanName : candidateNames) { candidates.put(beanName, getBean(beanName, requiredType, args)); } String primaryCandidate = determinePrimaryCandidate(candidates, requiredType); if (primaryCandidate != null) { - return getBean(primaryCandidate, requiredType, args); + return (T) candidates.get(primaryCandidate); } String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType); if (priorityCandidate != null) { - return getBean(priorityCandidate, requiredType, args); + return (T) candidates.get(priorityCandidate); } throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet()); } - else if (getParentBeanFactory() != null) { - return getParentBeanFactory().getBean(requiredType, args); - } - else { - throw new NoSuchBeanDefinitionException(requiredType); + + BeanFactory parent = getParentBeanFactory(); + if (parent != null) { + return parent.getBean(requiredType, args); } + throw new NoSuchBeanDefinitionException(requiredType); } @@ -615,10 +617,12 @@ public A findAnnotationOnBean(String beanName, Class a @Override public void registerResolvableDependency(Class dependencyType, Object autowiredValue) { - Assert.notNull(dependencyType, "Type must not be null"); + Assert.notNull(dependencyType, "Dependency type must not be null"); if (autowiredValue != null) { - Assert.isTrue((autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue)), - "Value [" + autowiredValue + "] does not implement specified type [" + dependencyType.getName() + "]"); + if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) { + throw new IllegalArgumentException("Value [" + autowiredValue + + "] does not implement specified dependency type [" + dependencyType.getName() + "]"); + } this.resolvableDependencies.put(dependencyType, autowiredValue); } } @@ -648,13 +652,15 @@ protected boolean isAutowireCandidate(String beanName, DependencyDescriptor desc else if (containsSingleton(beanName)) { return isAutowireCandidate(beanName, new RootBeanDefinition(getType(beanName)), descriptor, resolver); } - else if (getParentBeanFactory() instanceof DefaultListableBeanFactory) { + + BeanFactory parent = getParentBeanFactory(); + if (parent instanceof DefaultListableBeanFactory) { // No bean definition found in this factory -> delegate to parent. - return ((DefaultListableBeanFactory) getParentBeanFactory()).isAutowireCandidate(beanName, descriptor, resolver); + return ((DefaultListableBeanFactory) parent).isAutowireCandidate(beanName, descriptor, resolver); } - else if (getParentBeanFactory() instanceof ConfigurableListableBeanFactory) { + else if (parent instanceof ConfigurableListableBeanFactory) { // If no DefaultListableBeanFactory, can't pass the resolver along. - return ((ConfigurableListableBeanFactory) getParentBeanFactory()).isAutowireCandidate(beanName, descriptor); + return ((ConfigurableListableBeanFactory) parent).isAutowireCandidate(beanName, descriptor); } else { return true; @@ -995,23 +1001,24 @@ private void clearByTypeCache() { //--------------------------------------------------------------------- @Override - public Object resolveDependency(DependencyDescriptor descriptor, String beanName, + public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException { descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); - if (descriptor.getDependencyType().equals(javaUtilOptionalClass)) { - return new OptionalDependencyFactory().createOptionalDependency(descriptor, beanName); + if (javaUtilOptionalClass == descriptor.getDependencyType()) { + return new OptionalDependencyFactory().createOptionalDependency(descriptor, requestingBeanName); } else if (ObjectFactory.class == descriptor.getDependencyType()) { - return new DependencyObjectFactory(descriptor, beanName); + return new DependencyObjectFactory(descriptor, requestingBeanName); } else if (javaxInjectProviderClass == descriptor.getDependencyType()) { - return new DependencyProviderFactory().createDependencyProvider(descriptor, beanName); + return new DependencyProviderFactory().createDependencyProvider(descriptor, requestingBeanName); } else { - Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, beanName); + Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( + descriptor, requestingBeanName); if (result == null) { - result = doResolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter); + result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } @@ -1316,9 +1323,9 @@ protected boolean isPrimary(String beanName, Object beanInstance) { if (containsBeanDefinition(beanName)) { return getMergedLocalBeanDefinition(beanName).isPrimary(); } - BeanFactory parentFactory = getParentBeanFactory(); - return (parentFactory instanceof DefaultListableBeanFactory && - ((DefaultListableBeanFactory) parentFactory).isPrimary(beanName, beanInstance)); + BeanFactory parent = getParentBeanFactory(); + return (parent instanceof DefaultListableBeanFactory && + ((DefaultListableBeanFactory) parent).isPrimary(beanName, beanInstance)); } /** @@ -1471,7 +1478,7 @@ private class DependencyObjectFactory implements ObjectFactory, Serializ public DependencyObjectFactory(DependencyDescriptor descriptor, String beanName) { this.descriptor = new DependencyDescriptor(descriptor); this.descriptor.increaseNestingLevel(); - this.optional = this.descriptor.getDependencyType().equals(javaUtilOptionalClass); + this.optional = (this.descriptor.getDependencyType() == javaUtilOptionalClass); this.beanName = beanName; } @@ -1535,13 +1542,13 @@ public Object getOrderSource(Object obj) { if (beanDefinition == null) { return null; } - List sources = new ArrayList(); + List sources = new ArrayList(2); Method factoryMethod = beanDefinition.getResolvedFactoryMethod(); if (factoryMethod != null) { sources.add(factoryMethod); } Class targetType = beanDefinition.getTargetType(); - if (targetType != null && !targetType.equals(obj.getClass())) { + if (targetType != null && targetType != obj.getClass()) { sources.add(targetType); } return sources.toArray(new Object[sources.size()]); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java index 9042bad5cb..17b949a1c4 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -214,7 +214,7 @@ public Object getSingleton(String beanName, ObjectFactory singletonFactory) { if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, - "Singleton bean creation not allowed while the singletons of this factory are in destruction " + + "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { @@ -449,10 +449,10 @@ protected boolean isDependent(String beanName, String dependentBeanName) { } private boolean isDependent(String beanName, String dependentBeanName, Set alreadySeen) { - String canonicalName = canonicalName(beanName); if (alreadySeen != null && alreadySeen.contains(beanName)) { return false; } + String canonicalName = canonicalName(beanName); Set dependentBeans = this.dependentBeanMap.get(canonicalName); if (dependentBeans == null) { return false; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java index 4cb8f2e12d..d3a6c361c2 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -300,7 +300,7 @@ public Method run() { } } catch (IllegalArgumentException ex) { - throw new BeanDefinitionValidationException("Couldn't find a unique destroy method on bean with name '" + + throw new BeanDefinitionValidationException("Could not find unique destroy method on bean with name '" + this.beanName + ": " + ex.getMessage()); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java index 6df31aff78..11b98753e9 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ package org.springframework.beans.factory.support; import java.lang.reflect.Method; -import java.util.HashSet; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; @@ -35,7 +35,10 @@ */ public class MethodOverrides { - private final Set overrides = new LinkedHashSet(0); + private final Set overrides = + Collections.synchronizedSet(new LinkedHashSet(0)); + + private volatile boolean modified = false; /** @@ -57,7 +60,8 @@ public MethodOverrides(MethodOverrides other) { */ public void addOverrides(MethodOverrides other) { if (other != null) { - this.overrides.addAll(other.getOverrides()); + this.modified = true; + this.overrides.addAll(other.overrides); } } @@ -65,6 +69,7 @@ public void addOverrides(MethodOverrides other) { * Add the given method override. */ public void addOverride(MethodOverride override) { + this.modified = true; this.overrides.add(override); } @@ -74,6 +79,7 @@ public void addOverride(MethodOverride override) { * @see MethodOverride */ public Set getOverrides() { + this.modified = true; return this.overrides; } @@ -81,7 +87,7 @@ public Set getOverrides() { * Return whether the set of method overrides is empty. */ public boolean isEmpty() { - return this.overrides.isEmpty(); + return (!this.modified || this.overrides.isEmpty()); } /** @@ -90,13 +96,18 @@ public boolean isEmpty() { * @return the method override, or {@code null} if none */ public MethodOverride getOverride(Method method) { - MethodOverride match = null; - for (MethodOverride candidate : this.overrides) { - if (candidate.matches(method)) { - match = candidate; + if (!this.modified) { + return null; + } + synchronized (this.overrides) { + MethodOverride match = null; + for (MethodOverride candidate : this.overrides) { + if (candidate.matches(method)) { + match = candidate; + } } + return match; } - return match; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java index d2ac971cbc..91ad5d8e96 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -125,7 +125,7 @@ public RootBeanDefinition(Class beanClass, int autowireMode, boolean dependen setBeanClass(beanClass); setAutowireMode(autowireMode); if (dependencyCheck && getResolvedAutowireMode() != AUTOWIRE_CONSTRUCTOR) { - setDependencyCheck(RootBeanDefinition.DEPENDENCY_CHECK_OBJECTS); + setDependencyCheck(DEPENDENCY_CHECK_OBJECTS); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java index 2d701ce7fa..6e064c5d5e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ import java.lang.annotation.Annotation; import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -59,7 +58,7 @@ public class StaticListableBeanFactory implements ListableBeanFactory { /** Map from bean name to bean instance */ - private final Map beans = new HashMap(); + private final Map beans = new LinkedHashMap(); /** @@ -265,7 +264,7 @@ public Map getBeansOfType(Class type, boolean includeNonSingle throws BeansException { boolean isFactoryType = (type != null && FactoryBean.class.isAssignableFrom(type)); - Map matches = new HashMap(); + Map matches = new LinkedHashMap(); for (Map.Entry entry : this.beans.entrySet()) { String beanName = entry.getKey(); diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ResourceBundleEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ResourceBundleEditor.java index 7a827c9481..7eabc102b7 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ResourceBundleEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ResourceBundleEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author 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,14 +24,13 @@ import org.springframework.util.StringUtils; /** - * {@link java.beans.PropertyEditor} implementation for + * {@link java.beans.PropertyEditor} implementation for standard JDK * {@link java.util.ResourceBundle ResourceBundles}. * - *

Only supports conversion from a String, but not - * to a String. + *

Only supports conversion from a String, but not to a String. * - * Find below some examples of using this class in a - * (properly configured) Spring container using XML-based metadata: + * Find below some examples of using this class in a (properly configured) + * Spring container using XML-based metadata: * *

 <bean id="errorDialog" class="...">
  *    <!--
@@ -62,19 +61,20 @@
  *    </property>
  * </bean>
* - *

Please note that this {@link java.beans.PropertyEditor} is - * not registered by default with any of the Spring infrastructure. + *

Please note that this {@link java.beans.PropertyEditor} is not + * registered by default with any of the Spring infrastructure. * *

Thanks to David Leal Valmana for the suggestion and initial prototype. * * @author Rick Evans + * @author Juergen Hoeller * @since 2.0 */ public class ResourceBundleEditor extends PropertyEditorSupport { /** - * The separator used to distinguish between the base name and the - * locale (if any) when {@link #setAsText(String) converting from a String}. + * The separator used to distinguish between the base name and the locale + * (if any) when {@link #setAsText(String) converting from a String}. */ public static final String BASE_NAME_SEPARATOR = "_"; @@ -82,25 +82,23 @@ public class ResourceBundleEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { Assert.hasText(text, "'text' must not be empty"); - ResourceBundle bundle; - String rawBaseName = text.trim(); - int indexOfBaseNameSeparator = rawBaseName.indexOf(BASE_NAME_SEPARATOR); - if (indexOfBaseNameSeparator == -1) { - bundle = ResourceBundle.getBundle(rawBaseName); + String name = text.trim(); + + int separator = name.indexOf(BASE_NAME_SEPARATOR); + if (separator == -1) { + setValue(ResourceBundle.getBundle(name)); } else { - // it potentially has locale information - String baseName = rawBaseName.substring(0, indexOfBaseNameSeparator); + // The name potentially contains locale information + String baseName = name.substring(0, separator); if (!StringUtils.hasText(baseName)) { - throw new IllegalArgumentException("Bad ResourceBundle name : received '" + text + "' as argument to 'setAsText(String value)'."); + throw new IllegalArgumentException("Invalid ResourceBundle name: '" + text + "'"); } - String localeString = rawBaseName.substring(indexOfBaseNameSeparator + 1); + String localeString = name.substring(separator + 1); Locale locale = StringUtils.parseLocaleString(localeString); - bundle = (StringUtils.hasText(localeString)) - ? ResourceBundle.getBundle(baseName, locale) - : ResourceBundle.getBundle(baseName); + setValue((StringUtils.hasText(localeString)) ? ResourceBundle.getBundle(baseName, locale) : + ResourceBundle.getBundle(baseName)); } - setValue(bundle); } } diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java index 7a3232b1b1..a99bc92d89 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -194,6 +194,19 @@ public void getPropertyWithOptionalAndAutoGrow() { assertEquals("x", accessor.getPropertyValue("object.name")); } + @Test + public void incompletelyQuotedKeyLeadsToPropertyException() { + TestBean target = new TestBean(); + try { + BeanWrapper accessor = createAccessor(target); + accessor.setPropertyValue("[']", "foobar"); + fail("Should throw exception on invalid property"); + } + catch (NotWritablePropertyException ex) { + assertNull(ex.getPossibleMatches()); + } + } + @SuppressWarnings("unused") private static class GetterBean { @@ -212,6 +225,7 @@ public String getName() { } } + @SuppressWarnings("unused") private static class IntelliBean { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index e3f877d373..cbf2f37b92 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -2890,7 +2890,13 @@ public void close() { } - public static class BeanWithDestroyMethod { + public static abstract class BaseClassWithDestroyMethod { + + public abstract BaseClassWithDestroyMethod close(); + } + + + public static class BeanWithDestroyMethod extends BaseClassWithDestroyMethod { private static int closeCount = 0; @@ -2900,8 +2906,10 @@ public void setInner(BeanWithDestroyMethod inner) { this.inner = inner; } - public void close() { + @Override + public BeanWithDestroyMethod close() { closeCount++; + return this; } } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java index b6cb43db00..3983807c5b 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java @@ -45,6 +45,7 @@ import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.Ordered; +import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.Order; import org.springframework.tests.sample.beans.ITestBean; @@ -1397,6 +1398,8 @@ public void testGenericsBasedFieldInjectionWithSimpleMatch() { assertSame(1, bean.stringRepositoryMap.size()); assertSame(repo, bean.repositoryMap.get("repo")); assertSame(repo, bean.stringRepositoryMap.get("repo")); + + assertArrayEquals(new String[] {"repo"}, bf.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class))); } @Test @@ -1777,6 +1780,9 @@ public void testGenericsBasedInjectionIntoTypeVariableSelectingBestMatch() { GenericInterface1Impl bean1 = (GenericInterface1Impl) bf.getBean("bean1"); GenericInterface2Impl bean2 = (GenericInterface2Impl) bf.getBean("bean2"); assertSame(bean2, bean1.gi2); + + assertArrayEquals(new String[] {"bean1"}, bf.getBeanNamesForType(ResolvableType.forClassWithGenerics(GenericInterface1.class, String.class))); + assertArrayEquals(new String[] {"bean2"}, bf.getBeanNamesForType(ResolvableType.forClassWithGenerics(GenericInterface2.class, String.class))); } @Test @@ -2765,7 +2771,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl public interface GenericInterface1 { - public String doSomethingGeneric(T o); + String doSomethingGeneric(T o); } diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java index 2729922ad9..d4e19b8bae 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ public class ReaderEditorTests { @Test(expected = IllegalArgumentException.class) public void testCtorWithNullResourceEditor() throws Exception { - new InputStreamEditor(null); + new ReaderEditor(null); } @Test diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ResourceBundleEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ResourceBundleEditorTests.java index c6659d113e..613959dc3c 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ResourceBundleEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ResourceBundleEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2015 the original author 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,10 @@ * @author Rick Evans * @author Chris Beams */ -public final class ResourceBundleEditorTests { +public class ResourceBundleEditorTests { private static final String BASE_NAME = ResourceBundleEditorTests.class.getName(); + private static final String MESSAGE_KEY = "punk"; @@ -40,7 +41,8 @@ public void testSetAsTextWithJustBaseName() throws Exception { editor.setAsText(BASE_NAME); Object value = editor.getValue(); assertNotNull("Returned ResourceBundle was null (must not be for valid setAsText(..) call).", value); - assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", value instanceof ResourceBundle); + assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", + value instanceof ResourceBundle); ResourceBundle bundle = (ResourceBundle) value; String string = bundle.getString(MESSAGE_KEY); assertEquals(MESSAGE_KEY, string); @@ -52,7 +54,8 @@ public void testSetAsTextWithBaseNameThatEndsInDefaultSeparator() throws Excepti editor.setAsText(BASE_NAME + "_"); Object value = editor.getValue(); assertNotNull("Returned ResourceBundle was null (must not be for valid setAsText(..) call).", value); - assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", value instanceof ResourceBundle); + assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", + value instanceof ResourceBundle); ResourceBundle bundle = (ResourceBundle) value; String string = bundle.getString(MESSAGE_KEY); assertEquals(MESSAGE_KEY, string); @@ -64,7 +67,8 @@ public void testSetAsTextWithBaseNameAndLanguageCode() throws Exception { editor.setAsText(BASE_NAME + "Lang" + "_en"); Object value = editor.getValue(); assertNotNull("Returned ResourceBundle was null (must not be for valid setAsText(..) call).", value); - assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", value instanceof ResourceBundle); + assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", + value instanceof ResourceBundle); ResourceBundle bundle = (ResourceBundle) value; String string = bundle.getString(MESSAGE_KEY); assertEquals("yob", string); @@ -76,7 +80,8 @@ public void testSetAsTextWithBaseNameLanguageAndCountryCode() throws Exception { editor.setAsText(BASE_NAME + "LangCountry" + "_en_GB"); Object value = editor.getValue(); assertNotNull("Returned ResourceBundle was null (must not be for valid setAsText(..) call).", value); - assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", value instanceof ResourceBundle); + assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", + value instanceof ResourceBundle); ResourceBundle bundle = (ResourceBundle) value; String string = bundle.getString(MESSAGE_KEY); assertEquals("chav", string); @@ -88,31 +93,32 @@ public void testSetAsTextWithTheKitchenSink() throws Exception { editor.setAsText(BASE_NAME + "LangCountryDialect" + "_en_GB_GLASGOW"); Object value = editor.getValue(); assertNotNull("Returned ResourceBundle was null (must not be for valid setAsText(..) call).", value); - assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", value instanceof ResourceBundle); + assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", + value instanceof ResourceBundle); ResourceBundle bundle = (ResourceBundle) value; String string = bundle.getString(MESSAGE_KEY); assertEquals("ned", string); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testSetAsTextWithNull() throws Exception { ResourceBundleEditor editor = new ResourceBundleEditor(); editor.setAsText(null); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testSetAsTextWithEmptyString() throws Exception { ResourceBundleEditor editor = new ResourceBundleEditor(); editor.setAsText(""); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testSetAsTextWithWhiteSpaceString() throws Exception { ResourceBundleEditor editor = new ResourceBundleEditor(); editor.setAsText(" "); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testSetAsTextWithJustSeparatorString() throws Exception { ResourceBundleEditor editor = new ResourceBundleEditor(); editor.setAsText("_"); diff --git a/spring-beans/src/test/java/org/springframework/tests/beans/CollectingReaderEventListener.java b/spring-beans/src/test/java/org/springframework/tests/beans/CollectingReaderEventListener.java index b7fa67912c..1445c9944e 100644 --- a/spring-beans/src/test/java/org/springframework/tests/beans/CollectingReaderEventListener.java +++ b/spring-beans/src/test/java/org/springframework/tests/beans/CollectingReaderEventListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author 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,20 +36,21 @@ */ public class CollectingReaderEventListener implements ReaderEventListener { - private final List defaults = new LinkedList(); + private final List defaults = new LinkedList<>(); - private final Map componentDefinitions = new LinkedHashMap<>(8); + private final Map componentDefinitions = new LinkedHashMap<>(8); - private final Map aliasMap = new LinkedHashMap<>(8); + private final Map> aliasMap = new LinkedHashMap<>(8); + + private final List imports = new LinkedList<>(); - private final List imports = new LinkedList(); @Override public void defaultsRegistered(DefaultsDefinition defaultsDefinition) { this.defaults.add(defaultsDefinition); } - public List getDefaults() { + public List getDefaults() { return Collections.unmodifiableList(this.defaults); } @@ -59,27 +60,27 @@ public void componentRegistered(ComponentDefinition componentDefinition) { } public ComponentDefinition getComponentDefinition(String name) { - return (ComponentDefinition) this.componentDefinitions.get(name); + return this.componentDefinitions.get(name); } public ComponentDefinition[] getComponentDefinitions() { - Collection collection = this.componentDefinitions.values(); - return (ComponentDefinition[]) collection.toArray(new ComponentDefinition[collection.size()]); + Collection collection = this.componentDefinitions.values(); + return collection.toArray(new ComponentDefinition[collection.size()]); } @Override public void aliasRegistered(AliasDefinition aliasDefinition) { - List aliases = (List) this.aliasMap.get(aliasDefinition.getBeanName()); - if(aliases == null) { - aliases = new ArrayList(); + List aliases = this.aliasMap.get(aliasDefinition.getBeanName()); + if (aliases == null) { + aliases = new ArrayList<>(); this.aliasMap.put(aliasDefinition.getBeanName(), aliases); } aliases.add(aliasDefinition); } - public List getAliases(String beanName) { - List aliases = (List) this.aliasMap.get(beanName); - return aliases == null ? null : Collections.unmodifiableList(aliases); + public List getAliases(String beanName) { + List aliases = this.aliasMap.get(beanName); + return (aliases != null ? Collections.unmodifiableList(aliases) : null); } @Override @@ -87,7 +88,7 @@ public void importProcessed(ImportDefinition importDefinition) { this.imports.add(importDefinition); } - public List getImports() { + public List getImports() { return Collections.unmodifiableList(this.imports); } diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestBean.java b/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestBean.java index da243abbc9..f1ee1d4df5 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestBean.java +++ b/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -223,7 +223,7 @@ public void setSpouse(ITestBean spouse) { @Override public ITestBean[] getSpouses() { - return (spouse != null ? new ITestBean[]{spouse} : null); + return (spouse != null ? new ITestBean[] {spouse} : null); } public String getTouchy() { diff --git a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java index 06c5a5e856..2287995b66 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -81,7 +81,7 @@ public void setConfigLocation(Resource configLocation) { /** * Set the name of the EhCache CacheManager (if a specific name is desired). - * @see net.sf.ehcache.CacheManager#setName(String) + * @see net.sf.ehcache.config.Configuration#setName(String) */ public void setCacheManagerName(String cacheManagerName) { this.cacheManagerName = cacheManagerName; @@ -126,12 +126,17 @@ public void setShared(boolean shared) { @Override public void afterPropertiesSet() throws CacheException { - logger.info("Initializing EhCache CacheManager"); + if (logger.isInfoEnabled()) { + logger.info("Initializing EhCache CacheManager" + + (this.cacheManagerName != null ? " '" + this.cacheManagerName + "'" : "")); + } + Configuration configuration = (this.configLocation != null ? EhCacheManagerUtils.parseConfiguration(this.configLocation) : ConfigurationFactory.parseConfiguration()); if (this.cacheManagerName != null) { configuration.setName(this.cacheManagerName); } + if (this.shared) { // Old-school EhCache singleton sharing... // No way to find out whether we actually created a new CacheManager @@ -178,7 +183,10 @@ public boolean isSingleton() { @Override public void destroy() { if (this.locallyManaged) { - logger.info("Shutting down EhCache CacheManager"); + if (logger.isInfoEnabled()) { + logger.info("Shutting down EhCache CacheManager" + + (this.cacheManagerName != null ? " '" + this.cacheManagerName + "'" : "")); + } this.cacheManager.shutdown(); } } diff --git a/spring-context-support/src/main/java/org/springframework/cache/ehcache/package-info.java b/spring-context-support/src/main/java/org/springframework/cache/ehcache/package-info.java index 4291b2deae..edb951a4ce 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/ehcache/package-info.java +++ b/spring-context-support/src/main/java/org/springframework/cache/ehcache/package-info.java @@ -7,6 +7,6 @@ *

Note: EhCache 3.x lives in a different package namespace * and is not covered by the traditional support classes here. * Instead, consider using it through JCache (JSR-107), with - * Spring's support in {@link org.springframework.cache.jcache}. + * Spring's support in {@code org.springframework.cache.jcache}. */ package org.springframework.cache.ehcache; diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/commonj/TimerManagerTaskScheduler.java b/spring-context-support/src/main/java/org/springframework/scheduling/commonj/TimerManagerTaskScheduler.java index b1d4b2207f..2d392dea32 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/commonj/TimerManagerTaskScheduler.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/commonj/TimerManagerTaskScheduler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -134,7 +134,7 @@ public boolean cancel(boolean mayInterruptIfRunning) { @Override public long getDelay(TimeUnit unit) { - return unit.convert(System.currentTimeMillis() - this.timer.getScheduledExecutionTime(), TimeUnit.MILLISECONDS); + return unit.convert(this.timer.getScheduledExecutionTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } @Override diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java index 666f591e00..03917ad577 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -237,7 +237,9 @@ public void afterPropertiesSet() throws ParseException { CronTriggerImpl cti = new CronTriggerImpl(); cti.setName(this.name); cti.setGroup(this.group); - cti.setJobKey(this.jobDetail.getKey()); + if (this.jobDetail != null) { + cti.setJobKey(this.jobDetail.getKey()); + } cti.setJobDataMap(this.jobDataMap); cti.setStartTime(this.startTime); cti.setCronExpression(this.cronExpression); diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/ResourceLoaderClassLoadHelper.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/ResourceLoaderClassLoadHelper.java index 18ab755d56..726f9732d7 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/ResourceLoaderClassLoadHelper.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/ResourceLoaderClassLoadHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author 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.scheduling.quartz; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -73,43 +72,50 @@ public void initialize() { } @Override - @SuppressWarnings("rawtypes") - public Class loadClass(String name) throws ClassNotFoundException { + public Class loadClass(String name) throws ClassNotFoundException { return this.resourceLoader.getClassLoader().loadClass(name); } @SuppressWarnings("unchecked") public Class loadClass(String name, Class clazz) throws ClassNotFoundException { - return loadClass(name); + return (Class) loadClass(name); } @Override public URL getResource(String name) { Resource resource = this.resourceLoader.getResource(name); - try { - return resource.getURL(); - } - catch (FileNotFoundException ex) { - return null; + if (resource.exists()) { + try { + return resource.getURL(); + } + catch (IOException ex) { + if (logger.isWarnEnabled()) { + logger.warn("Could not load " + resource); + } + return null; + } } - catch (IOException ex) { - logger.warn("Could not load " + resource); - return null; + else { + return getClassLoader().getResource(name); } } @Override public InputStream getResourceAsStream(String name) { Resource resource = this.resourceLoader.getResource(name); - try { - return resource.getInputStream(); - } - catch (FileNotFoundException ex) { - return null; + if (resource.exists()) { + try { + return resource.getInputStream(); + } + catch (IOException ex) { + if (logger.isWarnEnabled()) { + logger.warn("Could not load " + resource); + } + return null; + } } - catch (IOException ex) { - logger.warn("Could not load " + resource); - return null; + else { + return getClassLoader().getResourceAsStream(name); } } diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java index 573fba98c2..a2e85455de 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -228,7 +228,9 @@ public void afterPropertiesSet() { SimpleTriggerImpl sti = new SimpleTriggerImpl(); sti.setName(this.name); sti.setGroup(this.group); - sti.setJobKey(this.jobDetail.getKey()); + if (this.jobDetail != null) { + sti.setJobKey(this.jobDetail.getKey()); + } sti.setJobDataMap(this.jobDataMap); sti.setStartTime(this.startTime); sti.setRepeatInterval(this.repeatInterval); diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java index 6a0f737e60..e4dbef0d3e 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,8 +72,8 @@ public void setSchedulerContext(SchedulerContext schedulerContext) { @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { Object job = super.createJobInstance(bundle); - BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job); - if (isEligibleForPropertyPopulation(bw.getWrappedInstance())) { + if (isEligibleForPropertyPopulation(job)) { + BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job); MutablePropertyValues pvs = new MutablePropertyValues(); if (this.schedulerContext != null) { pvs.addPropertyValues(this.schedulerContext); diff --git a/spring-context-support/src/test/java/org/springframework/scheduling/quartz/CronTriggerFactoryBeanTests.java b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/CronTriggerFactoryBeanTests.java new file mode 100644 index 0000000000..5aa0142fc7 --- /dev/null +++ b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/CronTriggerFactoryBeanTests.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002-2016 the original author 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.scheduling.quartz; + +import java.text.ParseException; + +import org.junit.Test; +import org.quartz.CronTrigger; + +import static org.junit.Assert.*; + +/** + * @author Stephane Nicoll + */ +public class CronTriggerFactoryBeanTests { + + @Test + public void createWithoutJobDetail() throws ParseException { + CronTriggerFactoryBean factory = new CronTriggerFactoryBean(); + factory.setName("myTrigger"); + factory.setCronExpression("0 15 10 ? * *"); + factory.afterPropertiesSet(); + CronTrigger trigger = factory.getObject(); + assertEquals("0 15 10 ? * *", trigger.getCronExpression()); + } + +} diff --git a/spring-context-support/src/test/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBeanTests.java b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBeanTests.java new file mode 100644 index 0000000000..7021682b23 --- /dev/null +++ b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBeanTests.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2016 the original author 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.scheduling.quartz; + +import java.text.ParseException; + +import org.junit.Test; +import org.quartz.SimpleTrigger; + +import static org.junit.Assert.*; + +/** + * @author Stephane Nicoll + */ +public class SimpleTriggerFactoryBeanTests { + + @Test + public void createWithoutJobDetail() throws ParseException { + SimpleTriggerFactoryBean factory = new SimpleTriggerFactoryBean(); + factory.setName("myTrigger"); + factory.setRepeatCount(5); + factory.setRepeatInterval(1000L); + factory.afterPropertiesSet(); + SimpleTrigger trigger = factory.getObject(); + assertEquals(5, trigger.getRepeatCount()); + assertEquals(1000L, trigger.getRepeatInterval()); + } + +} diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/AnnotationCacheOperationSource.java b/spring-context/src/main/java/org/springframework/cache/annotation/AnnotationCacheOperationSource.java index 438c8e633f..418e652154 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/AnnotationCacheOperationSource.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/AnnotationCacheOperationSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author 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,8 +43,7 @@ * @since 3.1 */ @SuppressWarnings("serial") -public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource - implements Serializable { +public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable { private final boolean publicMethodsOnly; @@ -177,6 +176,7 @@ public int hashCode() { return this.annotationParsers.hashCode(); } + /** * Callback interface providing {@link CacheOperation} instance(s) based on * a given {@link CacheAnnotationParser}. @@ -184,10 +184,9 @@ public int hashCode() { protected interface CacheOperationProvider { /** - * Returns the {@link CacheOperation} instance(s) provided by the specified parser. - * + * Return the {@link CacheOperation} instance(s) provided by the specified parser. * @param parser the parser to use - * @return the cache operations or {@code null} if none is found + * @return the cache operations, or {@code null} if none found */ Collection getCacheOperations(CacheAnnotationParser parser); } diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurationSelector.java b/spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurationSelector.java index f99b40d756..f7f6fa4ec7 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurationSelector.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurationSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,7 +52,7 @@ public class CachingConfigurationSelector extends AdviceModeImportSelector result = new ArrayList(); result.add(AutoProxyRegistrar.class.getName()); result.add(ProxyCachingConfiguration.class.getName()); - if (jsr107Present && jCacheImplPresent) { + if (jsr107Present && jcacheImplPresent) { result.add(PROXY_JCACHE_CONFIGURATION_CLASS); } return result.toArray(new String[result.size()]); @@ -94,7 +94,7 @@ private String[] getProxyImports() { private String[] getAspectJImports() { List result = new ArrayList(); result.add(CACHE_ASPECT_CONFIGURATION_CLASS_NAME); - if (jsr107Present && jCacheImplPresent) { + if (jsr107Present && jcacheImplPresent) { result.add(JCACHE_ASPECT_CONFIGURATION_CLASS_NAME); } return result.toArray(new String[result.size()]); diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java b/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java index 5086c1a1df..511e4de60d 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author 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,14 +27,16 @@ import org.springframework.core.Ordered; /** - * Enables Spring's annotation-driven cache management capability, similar to - * the support found in Spring's {@code } XML namespace. To be used together + * Enables Spring's annotation-driven cache management capability, similar to the + * support found in Spring's {@code } XML namespace. To be used together * with @{@link org.springframework.context.annotation.Configuration Configuration} * classes as follows: + * *

  * @Configuration
  * @EnableCaching
  * public class AppConfig {
+ *
  *     @Bean
  *     public MyService myService() {
  *         // configure and return a class having @Cacheable methods
@@ -52,11 +54,15 @@
  *
  * 

For reference, the example above can be compared to the following Spring XML * configuration: + * *

  * {@code
  * 
+ *
  *     
+ *
  *     
+ *
  *     
  *         
  *             
@@ -66,8 +72,10 @@
  *             
  *         
  *     
+ *
  * 
  * }
+ * * In both of the scenarios above, {@code @EnableCaching} and {@code * } are responsible for registering the necessary Spring * components that power annotation-driven cache management, such as the @@ -90,12 +98,14 @@ * *

For those that wish to establish a more direct relationship between * {@code @EnableCaching} and the exact cache manager bean to be used, - * the {@link CachingConfigurer} callback interface may be implemented - notice the - * the {@code @Override}-annotated methods below: + * the {@link CachingConfigurer} callback interface may be implemented. + * Notice the the {@code @Override}-annotated methods below: + * *

  * @Configuration
  * @EnableCaching
  * public class AppConfig extends CachingConfigurerSupport {
+ *
  *     @Bean
  *     public MyService myService() {
  *         // configure and return a class having @Cacheable methods
@@ -118,6 +128,7 @@
  *         return new MyKeyGenerator();
  *     }
  * }
+ * * This approach may be desirable simply because it is more explicit, or it may be * necessary in order to distinguish between two {@code CacheManager} beans present in the * same container. diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java b/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java index 7a1011405f..d6992bd263 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author 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,10 @@ protected Collection parseCacheAnnotations(DefaultCacheConfig ca if (cachings != null) { ops = lazyInit(ops); for (Caching caching : cachings) { - ops.addAll(parseCachingAnnotation(ae, cachingConfig, caching)); + Collection cachingOps = parseCachingAnnotation(ae, cachingConfig, caching); + if (cachingOps != null) { + ops.addAll(cachingOps); + } } } @@ -195,7 +198,7 @@ DefaultCacheConfig getDefaultCacheConfig(Class target) { } private Collection getAnnotations(AnnotatedElement ae, Class annotationType) { - Collection anns = new ArrayList(2); + Collection anns = new ArrayList(1); // look at raw annotation A ann = ae.getAnnotation(annotationType); @@ -211,7 +214,7 @@ private Collection getAnnotations(AnnotatedElement ae, } } - return (anns.isEmpty() ? null : anns); + return (!anns.isEmpty() ? anns : null); } /** diff --git a/spring-context/src/main/java/org/springframework/cache/config/AnnotationDrivenCacheBeanDefinitionParser.java b/spring-context/src/main/java/org/springframework/cache/config/AnnotationDrivenCacheBeanDefinitionParser.java index 4ea5ccad0a..d9f90dc2f1 100644 --- a/spring-context/src/main/java/org/springframework/cache/config/AnnotationDrivenCacheBeanDefinitionParser.java +++ b/spring-context/src/main/java/org/springframework/cache/config/AnnotationDrivenCacheBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,11 +60,10 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser private static final String JCACHE_ASPECT_CLASS_NAME = "org.springframework.cache.aspectj.JCacheCacheAspect"; - private static final boolean jsr107Present = ClassUtils.isPresent( "javax.cache.Cache", AnnotationDrivenCacheBeanDefinitionParser.class.getClassLoader()); - private static final boolean jCacheImplPresent = ClassUtils.isPresent( + private static final boolean jcacheImplPresent = ClassUtils.isPresent( "org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource", AnnotationDrivenCacheBeanDefinitionParser.class.getClassLoader()); @@ -91,7 +90,7 @@ public BeanDefinition parse(Element element, ParserContext parserContext) { private void registerCacheAspect(Element element, ParserContext parserContext) { SpringCachingConfigurer.registerCacheAspect(element, parserContext); - if (jsr107Present && jCacheImplPresent) { // Register JCache aspect + if (jsr107Present && jcacheImplPresent) { JCacheCachingConfigurer.registerCacheAspect(element, parserContext); } } @@ -99,7 +98,7 @@ private void registerCacheAspect(Element element, ParserContext parserContext) { private void registerCacheAdvisor(Element element, ParserContext parserContext) { AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); SpringCachingConfigurer.registerCacheAdvisor(element, parserContext); - if (jsr107Present && jCacheImplPresent) { // Register JCache advisor + if (jsr107Present && jcacheImplPresent) { JCacheCachingConfigurer.registerCacheAdvisor(element, parserContext); } } diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractCacheInvoker.java b/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractCacheInvoker.java index 70ab3a5fe1..4d3da40022 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractCacheInvoker.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/AbstractCacheInvoker.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author 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,14 +31,16 @@ public abstract class AbstractCacheInvoker { private CacheErrorHandler errorHandler; + + protected AbstractCacheInvoker() { + this(new SimpleCacheErrorHandler()); + } + protected AbstractCacheInvoker(CacheErrorHandler errorHandler) { Assert.notNull("ErrorHandler must not be null"); this.errorHandler = errorHandler; } - protected AbstractCacheInvoker() { - this(new SimpleCacheErrorHandler()); - } /** * Set the {@link CacheErrorHandler} instance to use to handle errors @@ -56,6 +58,7 @@ public CacheErrorHandler getErrorHandler() { return this.errorHandler; } + /** * Execute {@link Cache#get(Object)} on the specified {@link Cache} and * invoke the error handler if an exception occurs. Return {@code null} @@ -67,9 +70,9 @@ protected Cache.ValueWrapper doGet(Cache cache, Object key) { try { return cache.get(key); } - catch (RuntimeException e) { - getErrorHandler().handleCacheGetError(e, cache, key); - return null; // If the exception is handled, return a cache miss + catch (RuntimeException ex) { + getErrorHandler().handleCacheGetError(ex, cache, key); + return null; // If the exception is handled, return a cache miss } } @@ -81,8 +84,8 @@ protected void doPut(Cache cache, Object key, Object result) { try { cache.put(key, result); } - catch (RuntimeException e) { - getErrorHandler().handleCachePutError(e, cache, key, result); + catch (RuntimeException ex) { + getErrorHandler().handleCachePutError(ex, cache, key, result); } } @@ -94,8 +97,8 @@ protected void doEvict(Cache cache, Object key) { try { cache.evict(key); } - catch (RuntimeException e) { - getErrorHandler().handleCacheEvictError(e, cache, key); + catch (RuntimeException ex) { + getErrorHandler().handleCacheEvictError(ex, cache, key); } } @@ -107,8 +110,8 @@ protected void doClear(Cache cache) { try { cache.clear(); } - catch (RuntimeException e) { - getErrorHandler().handleCacheClearError(e, cache); + catch (RuntimeException ex) { + getErrorHandler().handleCacheClearError(ex, cache); } } diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java index ecec4ffda9..04b6a040e8 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,13 +76,10 @@ * @since 3.1 */ public abstract class CacheAspectSupport extends AbstractCacheInvoker - implements InitializingBean, SmartInitializingSingleton, ApplicationContextAware { + implements ApplicationContextAware, InitializingBean, SmartInitializingSingleton { protected final Log logger = LogFactory.getLog(getClass()); - /** - * Cache of CacheOperationMetadata, keyed by {@link CacheOperationCacheKey}. - */ private final Map metadataCache = new ConcurrentHashMap(1024); @@ -273,7 +270,7 @@ else if (StringUtils.hasText(operation.getCacheManager())) { * Return a bean with the specified name and type. Used to resolve services that * are referenced by name in a {@link CacheOperation}. * @param beanName the name of the bean, as defined by the operation - * @param expectedType type type for the bean + * @param expectedType type for the bean * @return the bean matching that name * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException if such bean does not exist * @see CacheOperation#keyGenerator @@ -293,8 +290,7 @@ protected void clearMetadataCache() { } protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) { - // check whether aspect is enabled - // to cope with cases where the AJ is pulled in automatically + // Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically) if (this.initialized) { Class targetClass = getTargetClass(target); Collection operations = getCacheOperationSource().getCacheOperations(method, targetClass); @@ -374,18 +370,15 @@ private boolean hasCachePut(CacheOperationContexts contexts) { for (CacheOperationContext context : cachePutContexts) { try { if (!context.isConditionPassing(ExpressionEvaluator.RESULT_UNAVAILABLE)) { - excluded.add(context); + excluded.add(context); } } - catch (VariableNotAvailableException e) { - // Ignoring failure due to missing result, consider the cache put has - // to proceed + catch (VariableNotAvailableException ex) { + // Ignoring failure due to missing result, consider the cache put has to proceed } } - // check if all puts have been excluded by condition - return cachePutContexts.size() != excluded.size(); - - + // Check if all puts have been excluded by condition + return (cachePutContexts.size() != excluded.size()); } private void processCacheEvicts(Collection contexts, boolean beforeInvocation, Object result) { @@ -710,4 +703,5 @@ public int hashCode() { return (this.cacheOperation.hashCode() * 31 + this.methodCacheKey.hashCode()); } } + } diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvaluationContext.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvaluationContext.java index 8d65e15eae..213bcd06de 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvaluationContext.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvaluationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,8 @@ package org.springframework.cache.interceptor; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import org.springframework.context.expression.MethodBasedEvaluationContext; import org.springframework.core.ParameterNameDiscoverer; @@ -38,25 +38,27 @@ * * @author Costin Leau * @author Stephane Nicoll + * @author Juergen Hoeller * @since 3.1 */ class CacheEvaluationContext extends MethodBasedEvaluationContext { - private final List unavailableVariables; + private final Set unavailableVariables = new HashSet(1); - CacheEvaluationContext(Object rootObject, Method method, Object[] args, - ParameterNameDiscoverer paramDiscoverer) { - super(rootObject, method, args, paramDiscoverer); - this.unavailableVariables = new ArrayList(); + CacheEvaluationContext(Object rootObject, Method method, Object[] arguments, + ParameterNameDiscoverer parameterNameDiscoverer) { + + super(rootObject, method, arguments, parameterNameDiscoverer); } + /** - * Add the specified variable name as unavailable for that context. Any expression trying - * to access this variable should lead to an exception. - *

This permits the validation of expressions that could potentially a variable even - * when such variable isn't available yet. Any expression trying to use that variable should - * therefore fail to evaluate. + * Add the specified variable name as unavailable for that context. + * Any expression trying to access this variable should lead to an exception. + *

This permits the validation of expressions that could potentially a + * variable even when such variable isn't available yet. Any expression + * trying to use that variable should therefore fail to evaluate. */ public void addUnavailableVariable(String name) { this.unavailableVariables.add(name); diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java index 199791e6a9..3529438d78 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,8 +21,8 @@ * *

Does not provide a way to transmit checked exceptions but * provide a special exception that should be used to wrap any - * exception that was thrown by the underlying invocation. Callers - * are expected to handle this issue type specifically. + * exception that was thrown by the underlying invocation. + * Callers are expected to handle this issue type specifically. * * @author Stephane Nicoll * @since 4.1 @@ -30,19 +30,19 @@ public interface CacheOperationInvoker { /** - * Invoke the cache operation defined by this instance. Wraps any - * exception that is thrown during the invocation in a - * {@link ThrowableWrapper}. + * Invoke the cache operation defined by this instance. Wraps any exception + * that is thrown during the invocation in a {@link ThrowableWrapper}. * @return the result of the operation * @throws ThrowableWrapper if an error occurred while invoking the operation */ Object invoke() throws ThrowableWrapper; + /** - * Wrap any exception thrown while invoking {@link #invoke()} + * Wrap any exception thrown while invoking {@link #invoke()}. */ @SuppressWarnings("serial") - public static class ThrowableWrapper extends RuntimeException { + class ThrowableWrapper extends RuntimeException { private final Throwable original; @@ -52,7 +52,7 @@ public ThrowableWrapper(Throwable original) { } public Throwable getOriginal() { - return original; + return this.original; } } diff --git a/spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java b/spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java index 09048cbe1f..98befaf841 100644 --- a/spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,6 +53,7 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life * Name of the ConversionService bean in the factory. * If none is supplied, default conversion rules apply. * @see org.springframework.core.convert.ConversionService + * @since 3.0 */ String CONVERSION_SERVICE_BEAN_NAME = "conversionService"; @@ -60,12 +61,14 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life * Name of the LoadTimeWeaver bean in the factory. If such a bean is supplied, * the context will use a temporary ClassLoader for type matching, in order * to allow the LoadTimeWeaver to process all actual bean classes. + * @since 2.5 * @see org.springframework.instrument.classloading.LoadTimeWeaver */ String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver"; /** * Name of the {@link Environment} bean in the factory. + * @since 3.1 */ String ENVIRONMENT_BEAN_NAME = "environment"; @@ -84,6 +87,7 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life /** * Set the unique id of this application context. + * @since 3.0 */ void setId(String id); @@ -99,6 +103,7 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life /** * Return the Environment for this application context in configurable form. + * @since 3.1 */ @Override ConfigurableEnvironment getEnvironment(); @@ -106,6 +111,7 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life /** * Set the {@code Environment} for this application context. * @param environment the new environment + * @since 3.1 */ void setEnvironment(ConfigurableEnvironment environment); @@ -113,9 +119,9 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life * Add a new BeanFactoryPostProcessor that will get applied to the internal * bean factory of this application context on refresh, before any of the * bean definitions get evaluated. To be invoked during context configuration. - * @param beanFactoryPostProcessor the factory processor to register + * @param postProcessor the factory processor to register */ - void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor); + void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor); /** * Add a new ApplicationListener that will be notified on context events diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java index 780f8be00b..da1fa78376 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -83,6 +83,7 @@ public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environmen AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } + /** * Return the BeanDefinitionRegistry that this scanner operates on. */ @@ -117,25 +118,52 @@ public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver (scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver()); } + + /** + * Register one or more annotated classes to be processed. + *

Calls to {@code register} are idempotent; adding the same + * annotated class more than once has no additional effect. + * @param annotatedClasses one or more annotated classes, + * e.g. {@link Configuration @Configuration} classes + */ public void register(Class... annotatedClasses) { for (Class annotatedClass : annotatedClasses) { registerBean(annotatedClass); } } + /** + * Register a bean from the given bean class, deriving its metadata from + * class-declared annotations. + * @param annotatedClass the class of the bean + */ + @SuppressWarnings("unchecked") public void registerBean(Class annotatedClass) { registerBean(annotatedClass, null, (Class[]) null); } - public void registerBean(Class annotatedClass, - @SuppressWarnings("unchecked") Class... qualifiers) { - + /** + * Register a bean from the given bean class, deriving its metadata from + * class-declared annotations. + * @param annotatedClass the class of the bean + * @param qualifiers specific qualifier annotations to consider, + * in addition to qualifiers at the bean class level + */ + @SuppressWarnings("unchecked") + public void registerBean(Class annotatedClass, Class... qualifiers) { registerBean(annotatedClass, null, qualifiers); } - public void registerBean(Class annotatedClass, String name, - @SuppressWarnings("unchecked") Class... qualifiers) { - + /** + * Register a bean from the given bean class, deriving its metadata from + * class-declared annotations. + * @param annotatedClass the class of the bean + * @param name an explicit name for the bean + * @param qualifiers specific qualifier annotations to consider, + * in addition to qualifiers at the bean class level + */ + @SuppressWarnings("unchecked") + public void registerBean(Class annotatedClass, String name, Class... qualifiers) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java index 98ab303e29..4d3b74f551 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -135,6 +135,16 @@ public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver this.scanner.setScopeMetadataResolver(scopeMetadataResolver); } + @Override + protected void prepareRefresh() { + this.scanner.clearCache(); + super.prepareRefresh(); + } + + + //--------------------------------------------------------------------- + // Implementation of AnnotationConfigRegistry + //--------------------------------------------------------------------- /** * Register one or more annotated classes to be processed. @@ -163,11 +173,4 @@ public void scan(String... basePackages) { this.scanner.scan(basePackages); } - - @Override - protected void prepareRefresh() { - this.scanner.clearCache(); - super.prepareRefresh(); - } - } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AutoProxyRegistrar.java b/spring-context/src/main/java/org/springframework/context/annotation/AutoProxyRegistrar.java index cc5c911c01..58b5481461 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AutoProxyRegistrar.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AutoProxyRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author 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 void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, B logger.warn(String.format("%s was imported but no annotations were found " + "having both 'mode' and 'proxyTargetClass' attributes of type " + "AdviceMode and boolean respectively. This means that auto proxy " + - "creator registration and configuration may not have occured as " + + "creator registration and configuration may not have occurred as " + "intended, and components may not be proxied as expected. Check to " + "ensure that %s has been @Import'ed on the same class where these " + "annotations are declared; otherwise remove the import of %s " + diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Bean.java b/spring-context/src/main/java/org/springframework/context/annotation/Bean.java index 5c0fcd6805..64cc2d0241 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Bean.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Bean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author 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,23 +39,25 @@ * public MyBean myBean() { * // instantiate and configure MyBean obj * return obj; - * }

+ * } + * * *

Bean Names

* - *

While a {@link #name() name} attribute is available, the default strategy for + *

While a {@link #name} attribute is available, the default strategy for * determining the name of a bean is to use the name of the {@code @Bean} method. * This is convenient and intuitive, but if explicit naming is desired, the - * {@code name} attribute may be used. Also note that {@code name} accepts an array - * of Strings. This is in order to allow for specifying multiple names (i.e., aliases) - * for a single bean. + * {@code name} attribute may be used. Also note that {@code name} accepts an + * array of Strings, allowing for multiple names (i.e. a primary bean name plus + * one or more aliases) for a single bean. * *

  *     @Bean(name={"b1","b2"}) // bean available as 'b1' and 'b2', but not 'myBean'
  *     public MyBean myBean() {
  *         // instantiate and configure MyBean obj
  *         return obj;
- *     }
+ * } + * * *

Scope, DependsOn, Primary, and Lazy

* @@ -70,14 +72,15 @@ * public MyBean myBean() { * // instantiate and configure MyBean obj * return obj; - * } + * } + * * *

{@code @Bean} Methods in {@code @Configuration} Classes

* *

Typically, {@code @Bean} methods are declared within {@code @Configuration} - * classes. In this case, bean methods may reference other {@code @Bean} methods - * in the same class by calling them directly. This ensures that references between - * beans are strongly typed and navigable. Such so-called 'inter-bean references' are + * classes. In this case, bean methods may reference other {@code @Bean} methods in the + * same class by calling them directly. This ensures that references between beans + * are strongly typed and navigable. Such so-called 'inter-bean references' are * guaranteed to respect scoping and AOP semantics, just like {@code getBean()} lookups * would. These are the semantics known from the original 'Spring JavaConfig' project * which require CGLIB subclassing of each such configuration class at runtime. As a @@ -87,14 +90,17 @@ *

  * @Configuration
  * public class AppConfig {
+ *
  *     @Bean
  *     public FooService fooService() {
  *         return new FooService(fooRepository());
  *     }
+ *
  *     @Bean
  *     public FooRepository fooRepository() {
  *         return new JdbcFooRepository(dataSource());
  *     }
+ *
  *     // ...
  * }
* @@ -152,7 +158,8 @@ * @Bean * public static PropertyPlaceholderConfigurer ppc() { * // instantiate, configure and return ppc ... - * } + * } + * * * By marking this method as {@code static}, it can be invoked without causing instantiation of its * declaring {@code @Configuration} class, thus avoiding the above-mentioned lifecycle conflicts. @@ -183,14 +190,21 @@ public @interface Bean { /** - * The name of this bean, or if plural, aliases for this bean. If left unspecified - * the name of the bean is the name of the annotated method. If specified, the method - * name is ignored. + * The name of this bean, or if several names, a primary bean name plus aliases. + *

If left unspecified, the name of the bean is the name of the annotated method. + * If specified, the method name is ignored. */ String[] name() default {}; /** * Are dependencies to be injected via convention-based autowiring by name or type? + *

Note that this autowire mode is just about externally driven autowiring based + * on bean property setter methods by convention, analogous to XML bean definitions. + *

The default mode does allow for annotation-driven autowiring. "no" refers to + * externally driven autowiring only, not affecting any autowiring demands that the + * bean class itself expresses through annotations. + * @see Autowire#BY_NAME + * @see Autowire#BY_TYPE */ Autowire autowire() default Autowire.NO; diff --git a/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java b/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java index f44d7b5051..626a328f41 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2015 the original author 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,17 +29,18 @@ class BeanAnnotationHelper { /** - * Return whether the given method is annotated directly or indirectly with @Bean. + * Return whether the given method is directly or indirectly annotated with + * the {@link Bean} annotation. */ public static boolean isBeanAnnotated(Method method) { - return AnnotationUtils.findAnnotation(method, Bean.class) != null; + return (AnnotationUtils.findAnnotation(method, Bean.class) != null); } public static String determineBeanNameFor(Method beanMethod) { - // by default the bean name is the name of the @Bean-annotated method + // By default, the bean name is the name of the @Bean-annotated method String beanName = beanMethod.getName(); - // check to see if the user has explicitly set the bean name + // Check to see if the user has explicitly set a custom bean name... Bean bean = AnnotationUtils.findAnnotation(beanMethod, Bean.class); if (bean != null && bean.name().length > 0) { beanName = bean.name()[0]; diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java index a1052fd6dc..f7ad662c42 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -125,10 +125,13 @@ /** * Specifies which types are eligible for component scanning. - *

Further narrows the set of candidate components from everything in - * {@link #basePackages} to everything in the base packages that matches - * the given filter or filters. - * @see #resourcePattern + *

Further narrows the set of candidate components from everything in {@link #basePackages} + * to everything in the base packages that matches the given filter or filters. + *

Note that these filters will be applied in addition to the default filters, if specified. + * Any type under the specified base packages which matches a given filter will be included, + * even if it does not match the default filters (i.e. is not annotated with {@code @Component}). + * @see #resourcePattern() + * @see #useDefaultFilters() */ Filter[] includeFilters() default {}; diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java b/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java index c8a8b75124..5d3b67779d 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author 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,14 @@ import org.springframework.stereotype.Component; /** - * Indicates that a class declares one or more {@link Bean @Bean} methods and may be processed - * by the Spring container to generate bean definitions and service requests for those - * beans at runtime, for example: + * Indicates that a class declares one or more {@link Bean @Bean} methods and + * may be processed by the Spring container to generate bean definitions and + * service requests for those beans at runtime, for example: + * *

  * @Configuration
  * public class AppConfig {
+ *
  *     @Bean
  *     public MyBean myBean() {
  *         // instantiate, configure and return bean ...
@@ -40,25 +42,28 @@
  * }
* *

Bootstrapping {@code @Configuration} classes

+ * *

Via {@code AnnotationConfigApplicationContext}

+ * * {@code @Configuration} classes are typically bootstrapped using either * {@link AnnotationConfigApplicationContext} or its web-capable variant, * {@link org.springframework.web.context.support.AnnotationConfigWebApplicationContext - * AnnotationConfigWebApplicationContext}. - * A simple example with the former follows: + * AnnotationConfigWebApplicationContext}. A simple example with the former follows: + * *
- * AnnotationConfigApplicationContext ctx =
- *     new AnnotationConfigApplicationContext();
+ * AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
  * ctx.register(AppConfig.class);
  * ctx.refresh();
  * MyBean myBean = ctx.getBean(MyBean.class);
- * // use myBean ...
+ * // use myBean ... + * * * See {@link AnnotationConfigApplicationContext} Javadoc for further details and see * {@link org.springframework.web.context.support.AnnotationConfigWebApplicationContext * AnnotationConfigWebApplicationContext} for {@code web.xml} configuration instructions. * *

Via Spring {@code } XML

+ * *

As an alternative to registering {@code @Configuration} classes directly against an * {@code AnnotationConfigApplicationContext}, {@code @Configuration} classes may be * declared as normal {@code } definitions within Spring XML files: @@ -74,6 +79,7 @@ * post processors that facilitate handling {@code @Configuration} classes. * *

Via component scanning

+ * *

{@code @Configuration} is meta-annotated with {@link Component @Component}, therefore * {@code @Configuration} classes are candidates for component scanning (typically using * Spring XML's {@code } element) and therefore may also take @@ -82,6 +88,7 @@ *

{@code @Configuration} classes may not only be bootstrapped using * component scanning, but may also themselves configure component scanning using * the {@link ComponentScan @ComponentScan} annotation: + * *

  * @Configuration
  * @ComponentScan("com.acme.app.services")
@@ -89,18 +96,20 @@
  *     // various @Bean definitions ...
  * }
* - * See {@link ComponentScan @ComponentScan} Javadoc for details. - * + * See the {@link ComponentScan @ComponentScan} javadoc for details. * *

Working with externalized values

+ * *

Using the {@code Environment} API

+ * * Externalized values may be looked up by injecting the Spring - * {@link org.springframework.core.env.Environment Environment} into a - * {@code @Configuration} class using the {@code @Autowired} or the {@code @Inject} - * annotation: + * {@link org.springframework.core.env.Environment} into a {@code @Configuration} + * class using the {@code @Autowired} or the {@code @Inject} annotation: + * *
  * @Configuration
  * public class AppConfig {
+ *
  *     @Inject Environment env;
  *
  *     @Bean
@@ -115,10 +124,12 @@
  * source" objects, and {@code @Configuration} classes may contribute property sources to
  * the {@code Environment} object using
  * the {@link org.springframework.core.env.PropertySources @PropertySources} annotation:
+ *
  * 
  * @Configuration
  * @PropertySource("classpath:/com/acme/app.properties")
  * public class AppConfig {
+ *
  *     @Inject Environment env;
  *
  *     @Bean
@@ -131,12 +142,15 @@
  * and {@link PropertySource @PropertySource} Javadoc for further details.
  *
  * 

Using the {@code @Value} annotation

+ * * Externalized values may be 'wired into' {@code @Configuration} classes using * the {@link Value @Value} annotation: + * *
  * @Configuration
  * @PropertySource("classpath:/com/acme/app.properties")
  * public class AppConfig {
+ *
  *     @Value("${bean.name}") String beanName;
  *
  *     @Bean
@@ -155,14 +169,18 @@
  * {@code PropertySourcesPlaceholderConfigurer}.
  *
  * 

Composing {@code @Configuration} classes

+ * *

With the {@code @Import} annotation

+ * *

{@code @Configuration} classes may be composed using the {@link Import @Import} annotation, * not unlike the way that {@code } works in Spring XML. Because * {@code @Configuration} objects are managed as Spring beans within the container, * imported configurations may be injected using {@code @Autowired} or {@code @Inject}: + * *

  * @Configuration
  * public class DatabaseConfig {
+ *
  *     @Bean
  *     public DataSource dataSource() {
  *         // instantiate, configure and return DataSource
@@ -172,6 +190,7 @@
  * @Configuration
  * @Import(DatabaseConfig.class)
  * public class AppConfig {
+ *
  *     @Inject DatabaseConfig dataConfig;
  *
  *     @Bean
@@ -188,13 +207,15 @@
  * new AnnotationConfigApplicationContext(AppConfig.class);
* *

With the {@code @Profile} annotation

+ * * {@code @Configuration} classes may be marked with the {@link Profile @Profile} annotation to - * indicate they should be processed only if a given profile or profiles are - * active: + * indicate they should be processed only if a given profile or profiles are active: + * *
  * @Profile("embedded")
  * @Configuration
  * public class EmbeddedDatabaseConfig {
+ *
  *     @Bean
  *     public DataSource dataSource() {
  *         // instantiate, configure and return embedded DataSource
@@ -204,25 +225,29 @@
  * @Profile("production")
  * @Configuration
  * public class ProductionDatabaseConfig {
+ *
  *     @Bean
  *     public DataSource dataSource() {
  *         // instantiate, configure and return production DataSource
  *     }
  * }
* - * See {@link Profile @Profile} and {@link org.springframework.core.env.Environment Environment} - * Javadoc for further details. + * See the {@link Profile @Profile} and {@link org.springframework.core.env.Environment} + * javadocs for further details. * *

With Spring XML using the {@code @ImportResource} annotation

+ * * As mentioned above, {@code @Configuration} classes may be declared as regular Spring * {@code } definitions within Spring XML files. It is also possible to * import Spring XML configuration files into {@code @Configuration} classes using * the {@link ImportResource @ImportResource} annotation. Bean definitions imported from XML can be * injected using {@code @Autowired} or {@code @Inject}: + * *
  * @Configuration
  * @ImportResource("classpath:/com/acme/database-config.xml")
  * public class AppConfig {
+ *
  *     @Inject DataSource dataSource; // from XML
  *
  *     @Bean
@@ -233,10 +258,13 @@
  * }
* *

With nested {@code @Configuration} classes

+ * * {@code @Configuration} classes may be nested within one another as follows: + * *
  * @Configuration
  * public class AppConfig {
+ *
  *     @Inject DataSource dataSource;
  *
  *     @Bean
@@ -264,6 +292,7 @@
  * enclosing {@code @Configuration} class.
  *
  * 

Configuring lazy initialization

+ * *

By default, {@code @Bean} methods will be eagerly instantiated at container * bootstrap time. To avoid this, {@code @Configuration} may be used in conjunction with * the {@link Lazy @Lazy} annotation to indicate that all {@code @Bean} methods declared within @@ -271,9 +300,11 @@ * individual {@code @Bean} methods as well. * *

Testing support for {@code @Configuration} classes

+ * * The Spring TestContext framework available in the {@code spring-test} module * provides the {@code @ContextConfiguration} annotation, which as of Spring 3.1 can * accept an array of {@code @Configuration} {@code Class} objects: + * *
  * @RunWith(SpringJUnit4ClassRunner.class)
  * @ContextConfiguration(classes={AppConfig.class, DatabaseConfig.class})
@@ -292,6 +323,7 @@
  * See TestContext framework reference documentation for details.
  *
  * 

Enabling built-in Spring features using {@code @Enable} annotations

+ * * Spring features such as asynchronous method execution, scheduled task execution, * annotation driven transaction management, and even Spring MVC can be enabled and * configured from {@code @Configuration} @@ -304,6 +336,7 @@ * for details. * *

Constraints when authoring {@code @Configuration} classes

+ * *