From 3231f458c8c2612825f65ffb9f078792af37acc5 Mon Sep 17 00:00:00 2001
From: Chris Beams
Date: Sat, 7 Mar 2009 04:54:31 +0000
Subject: [PATCH] + Added 'proxyMode' attribute to @Scope annotation +
Eliminated @ScopedProxy in favor of
@Scope(proxyMode=NO|INTERFACES|TARGET_CLASS)
---
.../config/java/BeanMethod.java | 21 ++++-
.../config/java/BeanRegistrar.java | 27 +++++-
.../InvalidScopedProxyDeclarationError.java | 33 -------
.../config/java/ScopedProxy.java | 92 -------------------
.../enhancement/BeanMethodInterceptor.java | 9 +-
.../lifecycle/scoping/ScopingTests.java | 44 ++++-----
.../context/annotation/Scope.java | 12 +++
7 files changed, 73 insertions(+), 165 deletions(-)
delete mode 100644 org.springframework.config.java/src/main/java/org/springframework/config/java/InvalidScopedProxyDeclarationError.java
delete mode 100644 org.springframework.config.java/src/main/java/org/springframework/config/java/ScopedProxy.java
diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanMethod.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanMethod.java
index 96077eb48d1..ddb4bdf4952 100644
--- a/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanMethod.java
+++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanMethod.java
@@ -27,6 +27,7 @@ import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.context.annotation.Scope;
+import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.io.FileSystemResource;
import org.springframework.util.Assert;
@@ -128,12 +129,11 @@ public final class BeanMethod {
if (Modifier.isFinal(getModifiers()))
problemReporter.error(new FinalMethodError());
- if (this.getAnnotation(ScopedProxy.class) == null)
- return;
-
Scope scope = this.getAnnotation(Scope.class);
- if(scope == null || scope.equals(SINGLETON) || scope.equals(PROTOTYPE))
- problemReporter.error(new InvalidScopedProxyDeclarationError(this));
+ if(scope != null
+ && scope.proxyMode() != ScopedProxyMode.NO
+ && (scope.value().equals(SINGLETON) || scope.value().equals(PROTOTYPE)))
+ problemReporter.error(new InvalidScopedProxyDeclarationError(this));
}
@Override
@@ -196,5 +196,16 @@ public final class BeanMethod {
super(format("method '%s' may not be final. remove the final modifier to continue", getName()), new Location(new FileSystemResource("/dev/null")));
}
}
+
+ public class InvalidScopedProxyDeclarationError extends Problem {
+ public InvalidScopedProxyDeclarationError(BeanMethod method) {
+ super(
+ String.format("method %s contains an invalid annotation declaration: scoped proxies "
+ + "cannot be created for singleton/prototype beans", method.getName()),
+ new Location(new FileSystemResource("/dev/null"))
+ );
+ }
+
+ }
}
\ No newline at end of file
diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanRegistrar.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanRegistrar.java
index 266123ef05c..dbed9f03293 100644
--- a/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanRegistrar.java
+++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanRegistrar.java
@@ -18,7 +18,9 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Scope;
+import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.util.Assert;
// TODO: SJC-242 document BeanHandler
@@ -26,6 +28,9 @@ import org.springframework.core.annotation.AnnotationUtils;
public class BeanRegistrar implements BeanDefinitionRegistrar {
private static final Log logger = LogFactory.getLog(BeanRegistrar.class);
+
+ /** Prefix used when registering the target object for a scoped proxy. */
+ private static final String TARGET_NAME_PREFIX = "scopedTarget.";
/**
* Ensures that member is a method and is annotated (directly or indirectly)
@@ -110,18 +115,17 @@ public class BeanRegistrar implements BeanDefinitionRegistrar {
if (hasText(destroyMethodName))
beanDef.setDestroyMethodName(destroyMethodName);
- // is this method annotated with @ScopedProxy?
- ScopedProxy scopedProxy = method.getAnnotation(ScopedProxy.class);
- if (scopedProxy != null) {
+ // is this method annotated with @Scope(scopedProxy=...)?
+ if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
RootBeanDefinition targetDef = beanDef;
//
// Create a scoped proxy definition for the original bean name,
// "hiding" the target bean in an internal target definition.
- String targetBeanName = ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
+ String targetBeanName = resolveHiddenScopedProxyBeanName(beanName);
RootBeanDefinition scopedProxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName);
- if (scopedProxy.proxyTargetClass())
+ if (scope.proxyMode() == ScopedProxyMode.TARGET_CLASS)
targetDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// ScopedFactoryBean's "proxyTargetClass" default is TRUE, so we
// don't need to set it explicitly here.
@@ -191,6 +195,19 @@ public class BeanRegistrar implements BeanDefinitionRegistrar {
+ "could be found in %s or its ancestry", beanName, registry));
}
+ /**
+ * Return the hidden name based on a scoped proxy bean name.
+ *
+ * @param originalBeanName the scope proxy bean name as declared in the
+ * Configuration-annotated class
+ *
+ * @return the internally-used hidden bean name
+ */
+ public static String resolveHiddenScopedProxyBeanName(String originalBeanName) {
+ Assert.hasText(originalBeanName);
+ return TARGET_NAME_PREFIX.concat(originalBeanName);
+ }
+
}
/**
diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/InvalidScopedProxyDeclarationError.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/InvalidScopedProxyDeclarationError.java
deleted file mode 100644
index 78894e33c05..00000000000
--- a/org.springframework.config.java/src/main/java/org/springframework/config/java/InvalidScopedProxyDeclarationError.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2002-2009 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.config.java;
-
-import org.springframework.beans.factory.parsing.Location;
-import org.springframework.beans.factory.parsing.Problem;
-import org.springframework.core.io.FileSystemResource;
-
-
-public class InvalidScopedProxyDeclarationError extends Problem {
- public InvalidScopedProxyDeclarationError(BeanMethod method) {
- super(
- String.format("method %s contains an invalid annotation declaration: @%s "
- + "cannot be used on a singleton/prototype bean", method.getName(), ScopedProxy.class
- .getSimpleName()),
- new Location(new FileSystemResource("/dev/null"))
- );
- }
-
-}
diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ScopedProxy.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ScopedProxy.java
deleted file mode 100644
index 92c94e59710..00000000000
--- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ScopedProxy.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2002-2008 the original author 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.config.java;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-import org.springframework.util.Assert;
-
-
-/**
- * Annotation identical in functionality with <aop:scoped-proxy/> tag. Provides a smart
- * proxy backed by a scoped bean, which can be injected into object instances (usually singletons)
- * allowing the same reference to be held while delegating method invocations to the backing, scoped
- * beans.
- *
- * Used with scoped beans (non-singleton and non-prototype).
- *
- *
- * @Configuration
- * public class ScopedConfig {
- *
- * @Bean(scope = "myScope")
- * @ScopedProxy
- * public SomeBean someBean() {
- * return new SomeBean();
- * }
- *
- * @Bean
- * public SomeOtherBean() {
- * return new AnotherBean(someBean());
- * }
- * }
- *
- *
- * See Spring reference
- * documentation for more
- * details.
- *
- * @author Costin Leau
- * @author Chris Beams
- * @since 3.0
- */
-@Target(ElementType.METHOD)
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-public @interface ScopedProxy {
-
- /**
- * Use CGLib-based class proxies (true) or JDK interface-based (false).
- *
- * Default is CGLib (true).
- * @return
- */
- boolean proxyTargetClass() default true;
-
- public static class Util {
-
- private static final String TARGET_NAME_PREFIX = "scopedTarget.";
-
- /**
- * Return the hidden name based on a scoped proxy bean name.
- *
- * @param originalBeanName the scope proxy bean name as declared in the
- * Configuration-annotated class
- *
- * @return the internally-used hidden bean name
- */
- public static String resolveHiddenScopedProxyBeanName(String originalBeanName) {
- Assert.hasText(originalBeanName);
- return TARGET_NAME_PREFIX.concat(originalBeanName);
- }
-
- }
-}
diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/BeanMethodInterceptor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/BeanMethodInterceptor.java
index a031d8caf32..c8d7d2b7738 100644
--- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/BeanMethodInterceptor.java
+++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/BeanMethodInterceptor.java
@@ -24,7 +24,8 @@ import net.sf.cglib.proxy.MethodProxy;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.config.java.Bean;
import org.springframework.config.java.BeanRegistrar;
-import org.springframework.config.java.ScopedProxy;
+import org.springframework.context.annotation.Scope;
+import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.annotation.AnnotationUtils;
@@ -46,11 +47,11 @@ class BeanMethodInterceptor extends AbstractMethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String beanName = getBeanName(method);
- boolean isScopedProxy =
- (AnnotationUtils.findAnnotation(method, ScopedProxy.class) != null);
+ Scope scope = AnnotationUtils.findAnnotation(method, Scope.class);
+ boolean isScopedProxy = (scope != null && scope.proxyMode() != ScopedProxyMode.NO);
String scopedBeanName =
- ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
+ BeanRegistrar.resolveHiddenScopedProxyBeanName(beanName);
if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName))
beanName = scopedBeanName;
diff --git a/org.springframework.config.java/src/test/java/test/feature/lifecycle/scoping/ScopingTests.java b/org.springframework.config.java/src/test/java/test/feature/lifecycle/scoping/ScopingTests.java
index 99f21a4e90d..8843958eb02 100644
--- a/org.springframework.config.java/src/test/java/test/feature/lifecycle/scoping/ScopingTests.java
+++ b/org.springframework.config.java/src/test/java/test/feature/lifecycle/scoping/ScopingTests.java
@@ -23,14 +23,15 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.aop.scope.ScopedObject;
-import org.springframework.beans.factory.config.Scope;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.config.java.Bean;
+import org.springframework.config.java.BeanRegistrar;
import org.springframework.config.java.Configuration;
-import org.springframework.config.java.ScopedProxy;
import org.springframework.config.java.StandardScopes;
import org.springframework.config.java.support.ConfigurationClassPostProcessor;
+import org.springframework.context.annotation.Scope;
+import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.context.support.GenericApplicationContext;
import test.beans.ITestBean;
@@ -40,8 +41,8 @@ import test.common.scope.CustomScope;
/**
- * Tests that scopes are properly supported by using a custom {@link Scope} and
- * {@link ScopedProxy} declarations.
+ * Tests that scopes are properly supported by using a custom Scope implementations
+ * and scoped proxy {@link Bean} declarations.
*
* @see ScopeIntegrationTests
* @author Costin Leau
@@ -68,7 +69,7 @@ public class ScopingTests {
customScope = null;
}
- private GenericApplicationContext createContext(Scope customScope, Class> configClass) {
+ private GenericApplicationContext createContext(org.springframework.beans.factory.config.Scope customScope, Class> configClass) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
if(customScope != null)
beanFactory.registerScope(SCOPE, customScope);
@@ -112,13 +113,13 @@ public class ScopingTests {
@Test
- public void testScopedProxyOnNonBeanAnnotatedMethod() throws Exception {
- // should throw - @ScopedProxy should not be applied on singleton/prototype beans
+ public void testScopedProxyOnSingletonBeanMethod() throws Exception {
+ // should throw - scoped proxies should not be applied on singleton/prototype beans
try {
createContext(null, InvalidProxyOnPredefinedScopesConfiguration.class);
fail("exception expected");
} catch (BeanDefinitionParsingException ex) {
- assertTrue(ex.getMessage().contains("cannot be used on a singleton/prototype bean"));
+ assertTrue(ex.getMessage().contains("scoped proxies cannot be created for singleton/prototype beans"));
}
}
@@ -144,7 +145,7 @@ public class ScopingTests {
String beanName = "scopedProxyInterface";
- String scopedBeanName = ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
+ String scopedBeanName = BeanRegistrar.resolveHiddenScopedProxyBeanName(beanName);
// get hidden bean
assertEquals(flag, spouse.getName());
@@ -178,7 +179,7 @@ public class ScopingTests {
String beanName = "scopedProxyClass";
- String scopedBeanName = ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
+ String scopedBeanName = BeanRegistrar.resolveHiddenScopedProxyBeanName(beanName);
// get hidden bean
assertEquals(flag, spouse.getName());
@@ -208,7 +209,7 @@ public class ScopingTests {
public void testScopedConfigurationBeanDefinitionCount() throws Exception {
// count the beans
- // 6 @Beans + 1 Configuration + 2 @ScopedProxy
+ // 6 @Beans + 1 Configuration + 2 scoped proxy
assertThat(ctx.getBeanDefinitionCount(), equalTo(9));
}
@@ -241,8 +242,7 @@ public class ScopingTests {
static class ScopeTestConfiguration {
@Bean
- @org.springframework.context.annotation.Scope(StandardScopes.SESSION)
- @ScopedProxy
+ @Scope(value=StandardScopes.SESSION, proxyMode=ScopedProxyMode.INTERFACES)
public Foo foo() {
return new Foo();
}
@@ -312,22 +312,16 @@ public class ScopingTests {
assertNotSame(message, newBean2, newBean3);
}
- @Configuration
- public static class InvalidProxyObjectConfiguration {
- @ScopedProxy
- public Object invalidProxyObject() { return new Object(); }
- }
-
@Configuration
public static class InvalidProxyOnPredefinedScopesConfiguration {
- @ScopedProxy @Bean
+ @Bean @Scope(proxyMode=ScopedProxyMode.INTERFACES)
public Object invalidProxyOnPredefinedScopes() { return new Object(); }
}
@Configuration
public static class ScopedConfigurationClass {
@Bean
- @org.springframework.context.annotation.Scope(SCOPE)
+ @Scope(SCOPE)
public TestBean scopedClass() {
TestBean tb = new TestBean();
tb.setName(flag);
@@ -335,7 +329,7 @@ public class ScopingTests {
}
@Bean
- @org.springframework.context.annotation.Scope(SCOPE)
+ @Scope(SCOPE)
public ITestBean scopedInterface() {
TestBean tb = new TestBean();
tb.setName(flag);
@@ -343,17 +337,15 @@ public class ScopingTests {
}
@Bean
- @org.springframework.context.annotation.Scope(SCOPE)
- @ScopedProxy(proxyTargetClass = false)
+ @Scope(value=SCOPE, proxyMode=ScopedProxyMode.TARGET_CLASS)
public ITestBean scopedProxyInterface() {
TestBean tb = new TestBean();
tb.setName(flag);
return tb;
}
- @ScopedProxy
@Bean
- @org.springframework.context.annotation.Scope(SCOPE)
+ @Scope(value=SCOPE, proxyMode=ScopedProxyMode.TARGET_CLASS)
public TestBean scopedProxyClass() {
TestBean tb = new TestBean();
tb.setName(flag);
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java
index dce7f65b628..4342dda1303 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java
@@ -44,5 +44,17 @@ public @interface Scope {
* @return the desired scope
*/
String value() default BeanDefinition.SCOPE_SINGLETON;
+
+ /**
+ * Specifies whether a component should be configured as a scoped proxy
+ * and if so, whether the proxy should be interface-based or subclass-based.
+ *
+ * Defaults to {@link ScopedProxyMode#NO}, indicating no scoped proxy
+ * should be created.
+ *
+ *
Analogous to {@literal } support in XML. Valid
+ * only in conjunction with a non-singleton, non-prototype {@link #value()}.
+ */
+ ScopedProxyMode proxyMode() default ScopedProxyMode.NO;
}