diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/Bean.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Bean.java
index 5189481da95..e9772936392 100644
--- a/org.springframework.config.java/src/main/java/org/springframework/config/java/Bean.java
+++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/Bean.java
@@ -57,6 +57,7 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
* @author Rod Johnson
* @author Costin Leau
* @author Chris Beams
+ * @since 3.0
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
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 983597ff8bc..23f5508a12a 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
@@ -25,7 +25,6 @@ import java.util.List;
import org.springframework.util.Assert;
-/** TODO: JAVADOC */
public final class BeanMethod implements Validatable {
private final String name;
@@ -91,8 +90,8 @@ public final class BeanMethod implements Validatable {
T anno = getAnnotation(annoType);
if (anno == null)
- throw new IllegalStateException(format("annotation %s not found on %s", annoType.getSimpleName(),
- this));
+ throw new IllegalStateException(
+ format("annotation %s not found on %s", annoType.getSimpleName(), this));
return anno;
}
@@ -123,8 +122,6 @@ public final class BeanMethod implements Validatable {
}
public void validate(List errors) {
-// for (Validator validator : validators)
-// validator.validate(this, errors);
if (Modifier.isPrivate(getModifiers()))
errors.add(new PrivateMethodError());
@@ -163,7 +160,7 @@ public final class BeanMethod implements Validatable {
public String toString() {
String returnTypeName = returnType == null ? "" : returnType.getSimpleName();
return String.format("%s: name=%s; returnType=%s; modifiers=%d", getClass().getSimpleName(), name,
- returnTypeName, modifiers);
+ returnTypeName, modifiers);
}
@Override
@@ -246,15 +243,14 @@ class BeanValidator implements Validator {
public void validate(Object object, List errors) {
BeanMethod method = (BeanMethod) object;
- // TODO: re-enable for @ScopedProxy support
- // if (method.getAnnotation(ScopedProxy.class) == null)
- // return;
- //
- // Bean bean = method.getRequiredAnnotation(Bean.class);
- //
- // if (bean.scope().equals(DefaultScopes.SINGLETON)
- // || bean.scope().equals(DefaultScopes.PROTOTYPE))
- // errors.add(new InvalidScopedProxyDeclarationError(method));
+ if (method.getAnnotation(ScopedProxy.class) == null)
+ return;
+
+ Bean bean = method.getRequiredAnnotation(Bean.class);
+
+ if (bean.scope().equals(StandardScopes.SINGLETON)
+ || bean.scope().equals(StandardScopes.PROTOTYPE))
+ errors.add(new InvalidScopedProxyDeclarationError(method));
}
}
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 30cb188f8e1..b9852c839df 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
@@ -7,6 +7,8 @@ import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
+import org.springframework.aop.scope.ScopedProxyFactoryBean;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -50,7 +52,7 @@ public class BeanRegistrar implements BeanDefinitionRegistrar {
if (bean.autowire() != AnnotationUtils.getDefaultValue(Bean.class, "autowire"))
beanDef.setAutowireMode(bean.autowire().value());
else if (defaults.defaultAutowire() != AnnotationUtils.getDefaultValue(Configuration.class,
- "defaultAutowire"))
+ "defaultAutowire"))
beanDef.setAutowireMode(defaults.defaultAutowire().value());
String beanName = method.getName();
@@ -70,9 +72,8 @@ public class BeanRegistrar implements BeanDefinitionRegistrar {
}
// overriding is legal, return immediately
- logger.info(format(
- "Skipping loading bean definition for %s: a definition for bean '%s' already exists. "
- + "This is likely due to an override in XML.", method, beanName));
+ logger.info(format("Skipping loading bean definition for %s: a definition for bean "
+ + "'%s' already exists. This is likely due to an override in XML.", method, beanName));
return;
}
}
@@ -104,39 +105,33 @@ public class BeanRegistrar implements BeanDefinitionRegistrar {
if (hasText(destroyMethodName))
beanDef.setDestroyMethodName(destroyMethodName);
- // TODO: re-enable for @ScopedProxy support
// is this method annotated with @ScopedProxy?
- // ScopedProxy scopedProxy = method.getAnnotation(ScopedProxy.class);
- // if (scopedProxy != null) {
- // 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);
- // RootBeanDefinition scopedProxyDefinition = new
- // RootBeanDefinition(ScopedProxyFactoryBean.class);
- // scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName",
- // targetBeanName);
- //
- // if (scopedProxy.proxyTargetClass())
- // 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.
- // else
- // scopedProxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass",
- // Boolean.FALSE);
- //
- // // The target bean should be ignored in favor of the scoped proxy.
- // targetDef.setAutowireCandidate(false);
- //
- // // Register the target bean as separate bean in the factory
- // registry.registerBeanDefinition(targetBeanName, targetDef);
- //
- // // replace the original bean definition with the target one
- // beanDef = scopedProxyDefinition;
- // }
+ ScopedProxy scopedProxy = method.getAnnotation(ScopedProxy.class);
+ if (scopedProxy != null) {
+ 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);
+ RootBeanDefinition scopedProxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
+ scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName);
+
+ if (scopedProxy.proxyTargetClass())
+ 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.
+ else
+ scopedProxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.FALSE);
+
+ // The target bean should be ignored in favor of the scoped proxy.
+ targetDef.setAutowireCandidate(false);
+
+ // Register the target bean as separate bean in the factory
+ registry.registerBeanDefinition(targetBeanName, targetDef);
+
+ // replace the original bean definition with the target one
+ beanDef = scopedProxyDefinition;
+ }
// TODO: re-enable for @Meta support
// does this bean method have any @Meta annotations?
@@ -147,8 +142,8 @@ public class BeanRegistrar implements BeanDefinitionRegistrar {
if (bean.dependsOn().length > 0)
beanDef.setDependsOn(bean.dependsOn());
- logger.info(format("Registering bean definition for @Bean method %s.%s()", configClass.getName(),
- beanName));
+ logger.info(format("Registering bean definition for @Bean method %s.%s()",
+ configClass.getName(), beanName));
registry.registerBeanDefinition(beanName, beanDef);
@@ -188,7 +183,7 @@ public class BeanRegistrar implements BeanDefinitionRegistrar {
} while (clbf != null);
throw new NoSuchBeanDefinitionException(format("No bean definition matching name '%s' "
- + "could be found in %s or its ancestry", beanName, registry));
+ + "could be found in %s or its ancestry", beanName, registry));
}
}
@@ -201,4 +196,4 @@ public class BeanRegistrar implements BeanDefinitionRegistrar {
*/
@SuppressWarnings("serial")
class ConfigurationClassBeanDefinition extends RootBeanDefinition {
-}
\ No newline at end of file
+}
diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/Configuration.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Configuration.java
index 1ea137739c4..1c6756f581b 100644
--- a/org.springframework.config.java/src/main/java/org/springframework/config/java/Configuration.java
+++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/Configuration.java
@@ -46,6 +46,7 @@ import org.springframework.stereotype.Component;
*
* @author Rod Johnson
* @author Chris Beams
+ * @since 3.0
*/
@Component
@Target( { ElementType.TYPE })
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
new file mode 100644
index 00000000000..4fc9dbe2e3d
--- /dev/null
+++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/InvalidScopedProxyDeclarationError.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+
+public class InvalidScopedProxyDeclarationError extends UsageError {
+ private final BeanMethod method;
+
+ public InvalidScopedProxyDeclarationError(BeanMethod method) {
+ super(method.getDeclaringClass(), method.getLineNumber());
+ this.method = method;
+ }
+
+ @Override
+ public String getDescription() {
+ return String.format("method %s contains an invalid annotation declaration: @%s "
+ + "cannot be used on a singleton/prototype bean", method.getName(), ScopedProxy.class
+ .getSimpleName());
+ }
+}
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
new file mode 100644
index 00000000000..92c94e59710
--- /dev/null
+++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ScopedProxy.java
@@ -0,0 +1,92 @@
+/*
+ * 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 46b8bb483f1..a031d8caf32 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,6 +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.core.annotation.AnnotationUtils;
/**
@@ -44,14 +46,13 @@ class BeanMethodInterceptor extends AbstractMethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String beanName = getBeanName(method);
- // TODO: re-enable for @ScopedProxy support
- // boolean isScopedProxy = (AnnotationUtils.findAnnotation(method,
- // ScopedProxy.class) != null);
- //
- // String scopedBeanName =
- // ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
- // if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName))
- // beanName = scopedBeanName;
+ boolean isScopedProxy =
+ (AnnotationUtils.findAnnotation(method, ScopedProxy.class) != null);
+
+ String scopedBeanName =
+ ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
+ if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName))
+ beanName = scopedBeanName;
if (factoryContainsBean(beanName)) {
// we have an already existing cached instance of this bean -> retrieve it
diff --git a/org.springframework.config.java/src/test/java/org/springframework/config/java/support/ConfigurationPostProcessorTests.java b/org.springframework.config.java/src/test/java/org/springframework/config/java/support/ConfigurationPostProcessorTests.java
index b9ff55d7f07..cb69af19cdd 100644
--- a/org.springframework.config.java/src/test/java/org/springframework/config/java/support/ConfigurationPostProcessorTests.java
+++ b/org.springframework.config.java/src/test/java/org/springframework/config/java/support/ConfigurationPostProcessorTests.java
@@ -110,7 +110,7 @@ public class ConfigurationPostProcessorTests {
* certain bean semantics, like singleton-scoping, scoped proxies, etc.
*
* Technically, {@link ConfigurationClassPostProcessor} could fail to enhance the
- * registered Configuration classes, and many use cases would still work.
+ * registered Configuration classes and many use cases would still work.
* Certain cases, however, like inter-bean singleton references would not.
* We test for such a case below, and in doing so prove that enhancement is
* working.
@@ -142,5 +142,5 @@ public class ConfigurationPostProcessorTests {
final Foo foo;
public Bar(Foo foo) { this.foo = foo; }
}
-
+
}
diff --git a/org.springframework.config.java/src/test/java/test/common/scope/CustomScope.java b/org.springframework.config.java/src/test/java/test/common/scope/CustomScope.java
new file mode 100644
index 00000000000..fa3d0ad67ae
--- /dev/null
+++ b/org.springframework.config.java/src/test/java/test/common/scope/CustomScope.java
@@ -0,0 +1,89 @@
+/*
+ * 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 test.common.scope;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.config.Scope;
+
+
+/**
+ * Simple scope implementation which creates object based on a flag.
+ *
+ * @author Costin Leau
+ * @author Chris Beams
+ */
+public class CustomScope implements Scope {
+
+ public boolean createNewScope = true;
+
+ private Map beans = new HashMap();
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.beans.factory.config.Scope#get(java.lang.String,
+ * org.springframework.beans.factory.ObjectFactory)
+ */
+ public Object get(String name, ObjectFactory> objectFactory) {
+ if (createNewScope) {
+ beans.clear();
+ // reset the flag back
+ createNewScope = false;
+ }
+
+ Object bean = beans.get(name);
+ // if a new object is requested or none exists under the current
+ // name, create one
+ if (bean == null) {
+ beans.put(name, objectFactory.getObject());
+ }
+
+ return beans.get(name);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.beans.factory.config.Scope#getConversationId()
+ */
+ public String getConversationId() {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.beans.factory.config.Scope#registerDestructionCallback(java.lang.String,
+ * java.lang.Runnable)
+ */
+ public void registerDestructionCallback(String name, Runnable callback) {
+ // do nothing
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.beans.factory.config.Scope#remove(java.lang.String)
+ */
+ public Object remove(String name) {
+ return beans.remove(name);
+ }
+
+ public Object resolveContextualObject(String key) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
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
new file mode 100644
index 00000000000..a5e0eb40bd3
--- /dev/null
+++ b/org.springframework.config.java/src/test/java/test/feature/lifecycle/scoping/ScopingTests.java
@@ -0,0 +1,374 @@
+/*
+ * 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 test.feature.lifecycle.scoping;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*;
+
+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.support.DefaultListableBeanFactory;
+import org.springframework.config.java.Bean;
+import org.springframework.config.java.Configuration;
+import org.springframework.config.java.InvalidScopedProxyDeclarationError;
+import org.springframework.config.java.MalformedConfigurationException;
+import org.springframework.config.java.ScopedProxy;
+import org.springframework.config.java.StandardScopes;
+import org.springframework.config.java.support.ConfigurationClassPostProcessor;
+import org.springframework.context.support.GenericApplicationContext;
+
+import test.beans.ITestBean;
+import test.beans.TestBean;
+import test.common.scope.CustomScope;
+
+
+
+/**
+ * Tests that scopes are properly supported by using a custom {@link Scope} and
+ * {@link ScopedProxy} declarations.
+ *
+ * @see ScopeIntegrationTests
+ * @author Costin Leau
+ * @author Chris Beams
+ */
+public class ScopingTests {
+
+ public static String flag = "1";
+
+ private static final String SCOPE = "my scope";
+ private CustomScope customScope;
+ private GenericApplicationContext ctx;
+
+ @Before
+ public void setUp() throws Exception {
+ customScope = new CustomScope();
+ ctx = createContext(customScope, ScopedConfigurationClass.class);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ ctx.close();
+ ctx = null;
+ customScope = null;
+ }
+
+ private GenericApplicationContext createContext(Scope customScope, Class> configClass) {
+ DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
+ if(customScope != null)
+ beanFactory.registerScope(SCOPE, customScope);
+ beanFactory.registerBeanDefinition("config",
+ rootBeanDefinition(configClass).getBeanDefinition());
+ GenericApplicationContext ctx = new GenericApplicationContext(beanFactory);
+ ctx.addBeanFactoryPostProcessor(new ConfigurationClassPostProcessor());
+ ctx.refresh();
+ return ctx;
+ }
+
+
+ @Test
+ public void testScopeOnClasses() throws Exception {
+ genericTestScope("scopedClass");
+ }
+
+
+ @Test
+ public void testScopeOnInterfaces() throws Exception {
+ genericTestScope("scopedInterface");
+ }
+
+
+ @Test
+ public void testSameScopeOnDifferentBeans() throws Exception {
+ Object beanAInScope = ctx.getBean("scopedClass");
+ Object beanBInScope = ctx.getBean("scopedInterface");
+
+ assertNotSame(beanAInScope, beanBInScope);
+
+ customScope.createNewScope = true;
+
+ Object newBeanAInScope = ctx.getBean("scopedClass");
+ Object newBeanBInScope = ctx.getBean("scopedInterface");
+
+ assertNotSame(newBeanAInScope, newBeanBInScope);
+ assertNotSame(newBeanAInScope, beanAInScope);
+ assertNotSame(newBeanBInScope, beanBInScope);
+ }
+
+
+ @Test
+ public void testScopedProxyOnNonBeanAnnotatedMethod() throws Exception {
+ // should throw - @ScopedProxy should not be applied on singleton/prototype beans
+ try {
+ createContext(null, InvalidProxyOnPredefinedScopesConfiguration.class);
+ fail("exception expected");
+ } catch (MalformedConfigurationException ex) {
+ assertTrue(ex.containsError(InvalidScopedProxyDeclarationError.class));
+ }
+ }
+
+
+ @Test
+ public void testRawScopes() throws Exception {
+
+ String beanName = "scopedProxyInterface";
+
+ // get hidden bean
+ Object bean = ctx.getBean("scopedTarget." + beanName);
+
+ assertFalse(bean instanceof ScopedObject);
+ }
+
+
+ @Test
+ public void testScopedProxyConfiguration() throws Exception {
+
+ TestBean singleton = (TestBean) ctx.getBean("singletonWithScopedInterfaceDep");
+ ITestBean spouse = singleton.getSpouse();
+ assertTrue("scoped bean is not wrapped by the scoped-proxy", spouse instanceof ScopedObject);
+
+ String beanName = "scopedProxyInterface";
+
+ String scopedBeanName = ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
+
+ // get hidden bean
+ assertEquals(flag, spouse.getName());
+
+ ITestBean spouseFromBF = (ITestBean) ctx.getBean(scopedBeanName);
+ assertEquals(spouse.getName(), spouseFromBF.getName());
+ // the scope proxy has kicked in
+ assertNotSame(spouse, spouseFromBF);
+
+ // create a new bean
+ customScope.createNewScope = true;
+
+ // get the bean again from the BF
+ spouseFromBF = (ITestBean) ctx.getBean(scopedBeanName);
+ // make sure the name has been updated
+ assertSame(spouse.getName(), spouseFromBF.getName());
+ assertNotSame(spouse, spouseFromBF);
+
+ // get the bean again
+ spouseFromBF = (ITestBean) ctx.getBean(scopedBeanName);
+ assertSame(spouse.getName(), spouseFromBF.getName());
+ }
+
+
+ @Test
+ public void testScopedProxyConfigurationWithClasses() throws Exception {
+
+ TestBean singleton = (TestBean) ctx.getBean("singletonWithScopedClassDep");
+ ITestBean spouse = singleton.getSpouse();
+ assertTrue("scoped bean is not wrapped by the scoped-proxy", spouse instanceof ScopedObject);
+
+ String beanName = "scopedProxyClass";
+
+ String scopedBeanName = ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName);
+
+ // get hidden bean
+ assertEquals(flag, spouse.getName());
+
+ TestBean spouseFromBF = (TestBean) ctx.getBean(scopedBeanName);
+ assertEquals(spouse.getName(), spouseFromBF.getName());
+ // the scope proxy has kicked in
+ assertNotSame(spouse, spouseFromBF);
+
+ // create a new bean
+ customScope.createNewScope = true;
+ flag = "boo";
+
+ // get the bean again from the BF
+ spouseFromBF = (TestBean) ctx.getBean(scopedBeanName);
+ // make sure the name has been updated
+ assertSame(spouse.getName(), spouseFromBF.getName());
+ assertNotSame(spouse, spouseFromBF);
+
+ // get the bean again
+ spouseFromBF = (TestBean) ctx.getBean(scopedBeanName);
+ assertSame(spouse.getName(), spouseFromBF.getName());
+ }
+
+
+ @Test
+ public void testScopedConfigurationBeanDefinitionCount() throws Exception {
+
+ // count the beans
+ // 6 @Beans + 1 Configuration + 2 @ScopedProxy
+ assertThat(ctx.getBeanDefinitionCount(), equalTo(9));
+ }
+
+// /**
+// * SJC-254 caught a regression in handling scoped proxies starting in 1.0 m4.
+// * The ScopedProxyFactoryBean object was having its scope set to that of its delegate
+// * whereas it should have remained singleton.
+// */
+// @Test
+// public void sjc254() {
+// JavaConfigWebApplicationContext ctx = new JavaConfigWebApplicationContext();
+// ctx.setConfigLocations(new String[] { ScopeTestConfiguration.class.getName() });
+// ctx.refresh();
+//
+// // should be fine
+// ctx.getBean(Bar.class);
+//
+// boolean threw = false;
+// try {
+// ctx.getBean(Foo.class);
+// } catch (BeanCreationException ex) {
+// if(ex.getCause() instanceof IllegalStateException) {
+// threw = true;
+// }
+// }
+// assertTrue(threw);
+// }
+
+ @Configuration
+ static class ScopeTestConfiguration {
+
+ @Bean(scope = StandardScopes.SESSION)
+ @ScopedProxy
+ public Foo foo() {
+ return new Foo();
+ }
+
+ @Bean
+ public Bar bar() {
+ return new Bar(foo());
+ }
+ }
+
+ static class Foo {
+ public Foo() {
+ //System.out.println("created foo: " + this.getClass().getName());
+ }
+
+ public void doSomething() {
+ //System.out.println("interesting: " + this);
+ }
+ }
+
+ static class Bar {
+
+ private final Foo foo;
+
+ public Bar(Foo foo) {
+ this.foo = foo;
+ //System.out.println("created bar: " + this);
+ }
+
+ public Foo getFoo() {
+ return foo;
+ }
+
+ }
+
+ private void genericTestScope(String beanName) throws Exception {
+ String message = "scope is ignored";
+ Object bean1 = ctx.getBean(beanName);
+ Object bean2 = ctx.getBean(beanName);
+
+ assertSame(message, bean1, bean2);
+
+ Object bean3 = ctx.getBean(beanName);
+
+ assertSame(message, bean1, bean3);
+
+ // make the scope create a new object
+ customScope.createNewScope = true;
+
+ Object newBean1 = ctx.getBean(beanName);
+ assertNotSame(message, bean1, newBean1);
+
+ Object sameBean1 = ctx.getBean(beanName);
+
+ assertSame(message, newBean1, sameBean1);
+
+ // make the scope create a new object
+ customScope.createNewScope = true;
+
+ Object newBean2 = ctx.getBean(beanName);
+ assertNotSame(message, newBean1, newBean2);
+
+ // make the scope create a new object .. again
+ customScope.createNewScope = true;
+
+ Object newBean3 = ctx.getBean(beanName);
+ assertNotSame(message, newBean2, newBean3);
+ }
+
+ @Configuration
+ public static class InvalidProxyObjectConfiguration {
+ @ScopedProxy
+ public Object invalidProxyObject() { return new Object(); }
+ }
+
+ @Configuration
+ public static class InvalidProxyOnPredefinedScopesConfiguration {
+ @ScopedProxy @Bean
+ public Object invalidProxyOnPredefinedScopes() { return new Object(); }
+ }
+
+ @Configuration
+ public static class ScopedConfigurationClass {
+ @Bean(scope = SCOPE)
+ public TestBean scopedClass() {
+ TestBean tb = new TestBean();
+ tb.setName(flag);
+ return tb;
+ }
+
+ @Bean(scope = SCOPE)
+ public ITestBean scopedInterface() {
+ TestBean tb = new TestBean();
+ tb.setName(flag);
+ return tb;
+ }
+
+ @Bean(scope = SCOPE)
+ @ScopedProxy(proxyTargetClass = false)
+ public ITestBean scopedProxyInterface() {
+ TestBean tb = new TestBean();
+ tb.setName(flag);
+ return tb;
+ }
+
+ @ScopedProxy
+ @Bean(scope = SCOPE)
+ public TestBean scopedProxyClass() {
+ TestBean tb = new TestBean();
+ tb.setName(flag);
+ return tb;
+ }
+
+ @Bean
+ public TestBean singletonWithScopedClassDep() {
+ TestBean singleton = new TestBean();
+ singleton.setSpouse(scopedProxyClass());
+ return singleton;
+ }
+
+ @Bean
+ public TestBean singletonWithScopedInterfaceDep() {
+ TestBean singleton = new TestBean();
+ singleton.setSpouse(scopedProxyInterface());
+ return singleton;
+ }
+ }
+
+}