Browse Source
git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@734 50f2f4bb-b051-0410-bef5-90022cba6387pull/1/head
10 changed files with 647 additions and 65 deletions
@ -0,0 +1,33 @@
@@ -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,89 @@
@@ -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 @@
@@ -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; |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue