Re-enabled support for @ScopedProxy
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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<UsageError> 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 ? "<unknown>" : 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<UsageError> 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ import org.springframework.stereotype.Component;
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
*/
|
||||
@Component
|
||||
@Target( { ElementType.TYPE })
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
* <p/>Used with scoped beans (non-singleton and non-prototype).</p>
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class ScopedConfig {
|
||||
*
|
||||
* @Bean(scope = "myScope")
|
||||
* @ScopedProxy
|
||||
* public SomeBean someBean() {
|
||||
* return new SomeBean();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public SomeOtherBean() {
|
||||
* return new AnotherBean(someBean());
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>See Spring reference <a href="http://static.springframework.org/spring/docs/2.5.x/reference/">
|
||||
* documentation</a> for more <a
|
||||
* href="http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-factory-scopes-other-injection">
|
||||
* details</a>.</p>
|
||||
*
|
||||
* @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 <i>hidden</i> 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 <i>hidden</i> bean name
|
||||
*/
|
||||
public static String resolveHiddenScopedProxyBeanName(String originalBeanName) {
|
||||
Assert.hasText(originalBeanName);
|
||||
return TARGET_NAME_PREFIX.concat(originalBeanName);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String, Object> beans = new HashMap<String, Object>();
|
||||
|
||||
/*
|
||||
* (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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user