1 /* 2 * Copyright 2002-2009 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.context.annotation.configuration; 18 19 import java.lang.annotation.ElementType; 20 import java.lang.annotation.Retention; 21 import java.lang.annotation.RetentionPolicy; 22 import java.lang.annotation.Target; 23 import java.util.HashMap; 24 import java.util.Map; 25 26 import org.junit.After; 27 import static org.junit.Assert.*; 28 import org.junit.Before; 29 import org.junit.Test; 30 import test.beans.ITestBean; 31 import test.beans.TestBean; 32 33 import org.springframework.aop.scope.ScopedObject; 34 import org.springframework.beans.factory.ObjectFactory; 35 import org.springframework.beans.factory.support.DefaultListableBeanFactory; 36 import org.springframework.beans.factory.support.RootBeanDefinition; 37 import org.springframework.context.annotation.Bean; 38 import org.springframework.context.annotation.Configuration; 39 import org.springframework.context.annotation.ConfigurationClassPostProcessor; 40 import org.springframework.context.annotation.Scope; 41 import org.springframework.context.annotation.ScopedProxyMode; 42 import org.springframework.context.support.GenericApplicationContext; 43 44 /** 45 * Tests that scopes are properly supported by using a custom Scope implementations 46 * and scoped proxy {@link Bean} declarations. 47 * 48 * @author Costin Leau 49 * @author Chris Beams 50 */ 51 public class ScopingTests { 52 53 public static String flag = "1"; 54 55 private static final String SCOPE = "my scope"; 56 57 private CustomScope customScope; 58 59 private GenericApplicationContext ctx; 60 61 @Before setUp()62 public void setUp() throws Exception { 63 customScope = new CustomScope(); 64 ctx = createContext(customScope, ScopedConfigurationClass.class); 65 } 66 67 @After tearDown()68 public void tearDown() throws Exception { 69 if (ctx != null) { 70 ctx.close(); 71 } 72 ctx = null; 73 customScope = null; 74 } 75 createContext(org.springframework.beans.factory.config.Scope customScope, Class<?> configClass)76 private GenericApplicationContext createContext(org.springframework.beans.factory.config.Scope customScope, Class<?> configClass) { 77 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); 78 if (customScope != null) { 79 beanFactory.registerScope(SCOPE, customScope); 80 } 81 beanFactory.registerBeanDefinition("config", new RootBeanDefinition(configClass)); 82 GenericApplicationContext ctx = new GenericApplicationContext(beanFactory); 83 ctx.addBeanFactoryPostProcessor(new ConfigurationClassPostProcessor()); 84 ctx.refresh(); 85 return ctx; 86 } 87 88 @Test testScopeOnClasses()89 public void testScopeOnClasses() throws Exception { 90 genericTestScope("scopedClass"); 91 } 92 93 @Test testScopeOnInterfaces()94 public void testScopeOnInterfaces() throws Exception { 95 genericTestScope("scopedInterface"); 96 } 97 98 @Test testSameScopeOnDifferentBeans()99 public void testSameScopeOnDifferentBeans() throws Exception { 100 Object beanAInScope = ctx.getBean("scopedClass"); 101 Object beanBInScope = ctx.getBean("scopedInterface"); 102 103 assertNotSame(beanAInScope, beanBInScope); 104 105 customScope.createNewScope = true; 106 107 Object newBeanAInScope = ctx.getBean("scopedClass"); 108 Object newBeanBInScope = ctx.getBean("scopedInterface"); 109 110 assertNotSame(newBeanAInScope, newBeanBInScope); 111 assertNotSame(newBeanAInScope, beanAInScope); 112 assertNotSame(newBeanBInScope, beanBInScope); 113 } 114 115 @Test testRawScopes()116 public void testRawScopes() throws Exception { 117 String beanName = "scopedProxyInterface"; 118 119 // get hidden bean 120 Object bean = ctx.getBean("scopedTarget." + beanName); 121 122 assertFalse(bean instanceof ScopedObject); 123 } 124 125 @Test testScopedProxyConfiguration()126 public void testScopedProxyConfiguration() throws Exception { 127 TestBean singleton = (TestBean) ctx.getBean("singletonWithScopedInterfaceDep"); 128 ITestBean spouse = singleton.getSpouse(); 129 assertTrue("scoped bean is not wrapped by the scoped-proxy", spouse instanceof ScopedObject); 130 131 String beanName = "scopedProxyInterface"; 132 133 String scopedBeanName = "scopedTarget." + beanName; 134 135 // get hidden bean 136 assertEquals(flag, spouse.getName()); 137 138 ITestBean spouseFromBF = (ITestBean) ctx.getBean(scopedBeanName); 139 assertEquals(spouse.getName(), spouseFromBF.getName()); 140 // the scope proxy has kicked in 141 assertNotSame(spouse, spouseFromBF); 142 143 // create a new bean 144 customScope.createNewScope = true; 145 146 // get the bean again from the BF 147 spouseFromBF = (ITestBean) ctx.getBean(scopedBeanName); 148 // make sure the name has been updated 149 assertSame(spouse.getName(), spouseFromBF.getName()); 150 assertNotSame(spouse, spouseFromBF); 151 152 // get the bean again 153 spouseFromBF = (ITestBean) ctx.getBean(scopedBeanName); 154 assertSame(spouse.getName(), spouseFromBF.getName()); 155 } 156 157 158 @Test testScopedProxyConfigurationWithClasses()159 public void testScopedProxyConfigurationWithClasses() throws Exception { 160 161 TestBean singleton = (TestBean) ctx.getBean("singletonWithScopedClassDep"); 162 ITestBean spouse = singleton.getSpouse(); 163 assertTrue("scoped bean is not wrapped by the scoped-proxy", spouse instanceof ScopedObject); 164 165 String beanName = "scopedProxyClass"; 166 167 String scopedBeanName = "scopedTarget." + beanName; 168 169 // get hidden bean 170 assertEquals(flag, spouse.getName()); 171 172 TestBean spouseFromBF = (TestBean) ctx.getBean(scopedBeanName); 173 assertEquals(spouse.getName(), spouseFromBF.getName()); 174 // the scope proxy has kicked in 175 assertNotSame(spouse, spouseFromBF); 176 177 // create a new bean 178 customScope.createNewScope = true; 179 flag = "boo"; 180 181 // get the bean again from the BF 182 spouseFromBF = (TestBean) ctx.getBean(scopedBeanName); 183 // make sure the name has been updated 184 assertSame(spouse.getName(), spouseFromBF.getName()); 185 assertNotSame(spouse, spouseFromBF); 186 187 // get the bean again 188 spouseFromBF = (TestBean) ctx.getBean(scopedBeanName); 189 assertSame(spouse.getName(), spouseFromBF.getName()); 190 } 191 192 193 @Test testScopedConfigurationBeanDefinitionCount()194 public void testScopedConfigurationBeanDefinitionCount() throws Exception { 195 // count the beans 196 // 6 @Beans + 1 Configuration + 2 scoped proxy + 1 importRegistry 197 assertEquals(10, ctx.getBeanDefinitionCount()); 198 } 199 200 // /** 201 // * SJC-254 caught a regression in handling scoped proxies starting in 1.0 m4. 202 // * The ScopedProxyFactoryBean object was having its scope set to that of its delegate 203 // * whereas it should have remained singleton. 204 // */ 205 // @Test 206 // public void sjc254() { 207 // JavaConfigWebApplicationContext ctx = new JavaConfigWebApplicationContext(); 208 // ctx.setConfigLocations(new String[] { ScopeTestConfiguration.class.getName() }); 209 // ctx.refresh(); 210 // 211 // // should be fine 212 // ctx.getBean(Bar.class); 213 // 214 // boolean threw = false; 215 // try { 216 // ctx.getBean(Foo.class); 217 // } catch (BeanCreationException ex) { 218 // if(ex.getCause() instanceof IllegalStateException) { 219 // threw = true; 220 // } 221 // } 222 // assertTrue(threw); 223 // } 224 225 @Configuration 226 static class ScopeTestConfiguration { 227 228 @Bean 229 @Scope(value="session", proxyMode=ScopedProxyMode.INTERFACES) foo()230 public Foo foo() { 231 return new Foo(); 232 } 233 234 @Bean bar()235 public Bar bar() { 236 return new Bar(foo()); 237 } 238 } 239 240 static class Foo { Foo()241 public Foo() { 242 //System.out.println("created foo: " + this.getClass().getName()); 243 } 244 doSomething()245 public void doSomething() { 246 //System.out.println("interesting: " + this); 247 } 248 } 249 250 static class Bar { 251 252 private final Foo foo; 253 Bar(Foo foo)254 public Bar(Foo foo) { 255 this.foo = foo; 256 //System.out.println("created bar: " + this); 257 } 258 getFoo()259 public Foo getFoo() { 260 return foo; 261 } 262 263 } 264 genericTestScope(String beanName)265 private void genericTestScope(String beanName) throws Exception { 266 String message = "scope is ignored"; 267 Object bean1 = ctx.getBean(beanName); 268 Object bean2 = ctx.getBean(beanName); 269 270 assertSame(message, bean1, bean2); 271 272 Object bean3 = ctx.getBean(beanName); 273 274 assertSame(message, bean1, bean3); 275 276 // make the scope create a new object 277 customScope.createNewScope = true; 278 279 Object newBean1 = ctx.getBean(beanName); 280 assertNotSame(message, bean1, newBean1); 281 282 Object sameBean1 = ctx.getBean(beanName); 283 284 assertSame(message, newBean1, sameBean1); 285 286 // make the scope create a new object 287 customScope.createNewScope = true; 288 289 Object newBean2 = ctx.getBean(beanName); 290 assertNotSame(message, newBean1, newBean2); 291 292 // make the scope create a new object .. again 293 customScope.createNewScope = true; 294 295 Object newBean3 = ctx.getBean(beanName); 296 assertNotSame(message, newBean2, newBean3); 297 } 298 299 @Configuration 300 public static class InvalidProxyOnPredefinedScopesConfiguration { 301 302 @Bean @Scope(proxyMode=ScopedProxyMode.INTERFACES) invalidProxyOnPredefinedScopes()303 public Object invalidProxyOnPredefinedScopes() { return new Object(); } 304 } 305 306 @Configuration 307 public static class ScopedConfigurationClass { 308 309 @Bean 310 @MyScope scopedClass()311 public TestBean scopedClass() { 312 TestBean tb = new TestBean(); 313 tb.setName(flag); 314 return tb; 315 } 316 317 @Bean 318 @MyScope scopedInterface()319 public ITestBean scopedInterface() { 320 TestBean tb = new TestBean(); 321 tb.setName(flag); 322 return tb; 323 } 324 325 @Bean 326 @MyProxiedScope scopedProxyInterface()327 public ITestBean scopedProxyInterface() { 328 TestBean tb = new TestBean(); 329 tb.setName(flag); 330 return tb; 331 } 332 333 @MyProxiedScope scopedProxyClass()334 public TestBean scopedProxyClass() { 335 TestBean tb = new TestBean(); 336 tb.setName(flag); 337 return tb; 338 } 339 340 @Bean singletonWithScopedClassDep()341 public TestBean singletonWithScopedClassDep() { 342 TestBean singleton = new TestBean(); 343 singleton.setSpouse(scopedProxyClass()); 344 return singleton; 345 } 346 347 @Bean singletonWithScopedInterfaceDep()348 public TestBean singletonWithScopedInterfaceDep() { 349 TestBean singleton = new TestBean(); 350 singleton.setSpouse(scopedProxyInterface()); 351 return singleton; 352 } 353 } 354 355 356 @Target({ElementType.METHOD}) 357 @Retention(RetentionPolicy.RUNTIME) 358 @Scope(SCOPE) 359 @interface MyScope { 360 } 361 362 363 @Target({ElementType.METHOD}) 364 @Retention(RetentionPolicy.RUNTIME) 365 @Bean 366 @Scope(value=SCOPE, proxyMode=ScopedProxyMode.TARGET_CLASS) 367 @interface MyProxiedScope { 368 } 369 370 371 /** 372 * Simple scope implementation which creates object based on a flag. 373 * @author Costin Leau 374 * @author Chris Beams 375 */ 376 static class CustomScope implements org.springframework.beans.factory.config.Scope { 377 378 public boolean createNewScope = true; 379 380 private Map<String, Object> beans = new HashMap<String, Object>(); 381 get(String name, ObjectFactory<?> objectFactory)382 public Object get(String name, ObjectFactory<?> objectFactory) { 383 if (createNewScope) { 384 beans.clear(); 385 // reset the flag back 386 createNewScope = false; 387 } 388 389 Object bean = beans.get(name); 390 // if a new object is requested or none exists under the current 391 // name, create one 392 if (bean == null) { 393 beans.put(name, objectFactory.getObject()); 394 } 395 396 return beans.get(name); 397 } 398 getConversationId()399 public String getConversationId() { 400 return null; 401 } 402 registerDestructionCallback(String name, Runnable callback)403 public void registerDestructionCallback(String name, Runnable callback) { 404 // do nothing 405 } 406 remove(String name)407 public Object remove(String name) { 408 return beans.remove(name); 409 } 410 resolveContextualObject(String key)411 public Object resolveContextualObject(String key) { 412 return null; 413 } 414 } 415 416 } 417