2013-10-16 83 views
2

我有一個簡單的JUnit測試的基礎上,AbstractJUnit4SpringContextTests類:春天JUnit測試無法正常重用的ApplicationContext

@ContextConfiguration(locations = { 
     "/test-spring-config.xml", "/test-databaseApplicationContext.xml", 
     "/test-sharedApplicationContext.xml", "/test-dispatcher-servlet.xml" 
}) 
public class TestTest extends AbstractJUnit4SpringContextTests { 

    @Test 
    public void testOneThing() { 

    } 
} 

,這是加載應用程序上下文的一部分,一個bean:

<bean id="mySingleton" class="com.company.SingletonClass" />

這個單身人士,因爲它在其他項目/地點使用,有一個構造函數,確保在給定的時間只有一個類的實例:

public class SingletonClass { 
    private static SingletonClass instance = null; 
    public SingletonClass() { 
     if (instance != null) { 
      throw new IllegalStateException("Highlander rules in effect."); 
     } 
     instance = this; 
    } 
} 

但是,當我運行我的JUnit測試時,此異常被命中。我明白從這個答案:Reuse spring application context across junit test classes

只要locations是相同的應用程序上下文被重用。但是,這似乎並非如此。這是其中的bean被實例化兩個位置:

第一:

Thread [main] (Suspended (breakpoint at line 47 in SingletonClass)) 
    SingletonClass.<init>() line: 47  
    NativeConstructorAccessorImpl.newInstance0(Constructor, Object[]) line: not available [native method] 
    NativeConstructorAccessorImpl.newInstance(Object[]) line: 57  
    DelegatingConstructorAccessorImpl.newInstance(Object[]) line: 45  
    Constructor<T>.newInstance(Object...) line: 525 
    BeanUtils.instantiateClass(Constructor<T>, Object...) line: 147 
    CglibSubclassingInstantiationStrategy(SimpleInstantiationStrategy).instantiate(RootBeanDefinition, String, BeanFactory) line: 76  
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).instantiateBean(String, RootBeanDefinition) line: 990  
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBeanInstance(String, RootBeanDefinition, Object[]) line: 943 
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 485 
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 456 
    AbstractBeanFactory$1.getObject() line: 294 
    DefaultListableBeanFactory(DefaultSingletonBeanRegistry).getSingleton(String, ObjectFactory) line: 225 
    DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 291  
    DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 193 
    DefaultListableBeanFactory.preInstantiateSingletons() line: 585 
    GenericApplicationContext(AbstractApplicationContext).finishBeanFactoryInitialization(ConfigurableListableBeanFactory) line: 913  
    GenericApplicationContext(AbstractApplicationContext).refresh() line: 464 
    GenericXmlContextLoader(AbstractGenericContextLoader).loadContext(MergedContextConfiguration) line: 103 
    GenericXmlContextLoader(AbstractGenericContextLoader).loadContext(MergedContextConfiguration) line: 1 
    DelegatingSmartContextLoader.loadContext(MergedContextConfiguration) line: 228 
    TestContext.loadApplicationContext() line: 124 
    TestContext.getApplicationContext() line: 148 
    DependencyInjectionTestExecutionListener.injectDependencies(TestContext) line: 109 
    DependencyInjectionTestExecutionListener.prepareTestInstance(TestContext) line: 75 
    TestContextManager.prepareTestInstance(Object) line: 321  
    SpringJUnit4ClassRunner.createTest() line: 211 
    SpringJUnit4ClassRunner$1.runReflectiveCall() line: 288 
    SpringJUnit4ClassRunner$1(ReflectiveCallable).run() line: 15  
    SpringJUnit4ClassRunner.methodBlock(FrameworkMethod) line: 290 
    SpringJUnit4ClassRunner.runChild(FrameworkMethod, RunNotifier) line: 231  
    SpringJUnit4ClassRunner(BlockJUnit4ClassRunner).runChild(Object, RunNotifier) line: 44 
    SpringJUnit4ClassRunner(ParentRunner<T>).runChildren(RunNotifier) line: 180 
    ParentRunner<T>.access$000(ParentRunner, RunNotifier) line: 41 
    ParentRunner$1.evaluate() line: 173 
    RunBefores.evaluate() line: 28 
    RunBeforeTestClassCallbacks.evaluate() line: 61 
    RunAfters.evaluate() line: 31 
    RunAfterTestClassCallbacks.evaluate() line: 71 
    SpringJUnit4ClassRunner(ParentRunner<T>).run(RunNotifier) line: 220 
    SpringJUnit4ClassRunner.run(RunNotifier) line: 174 
    JUnit4TestMethodReference(JUnit4TestReference).run(TestExecution) line: 50 
    TestExecution.run(ITestReference[]) line: 38  
    RemoteTestRunner.runTests(String[], String, TestExecution) line: 467  
    RemoteTestRunner.runTests(TestExecution) line: 683 
    RemoteTestRunner.run() line: 390  
    RemoteTestRunner.main(String[]) line: 197 

二:

Thread [main] (Suspended (breakpoint at line 47 in SingletonClass)) 
    SingletonClass$$EnhancerByCGLIB$$e8a4cc48(SingletonClass).<init>() line: 47 
    SingletonClass$$EnhancerByCGLIB$$e8a4cc48.<init>() line: not available 
    NativeConstructorAccessorImpl.newInstance0(Constructor, Object[]) line: not available [native method] 
    NativeConstructorAccessorImpl.newInstance(Object[]) line: 57  
    DelegatingConstructorAccessorImpl.newInstance(Object[]) line: 45  
    Constructor<T>.newInstance(Object...) line: 525 
    ReflectUtils.newInstance(Constructor, Object[]) line: 228 
    ReflectUtils.newInstance(Class, Class[], Object[]) line: 220  
    ReflectUtils.newInstance(Class) line: 216 
    Enhancer.createUsingReflection(Class) line: 643 
    Enhancer.firstInstance(Class) line: 538 
    Enhancer(AbstractClassGenerator).create(Object) line: 225 
    Enhancer.createHelper() line: 377 
    Enhancer.create() line: 285 
    Cglib2AopProxy.getProxy(ClassLoader) line: 201 
    ProxyFactory.getProxy(ClassLoader) line: 112  
    InfrastructureAdvisorAutoProxyCreator(AbstractAutoProxyCreator).createProxy(Class<?>, String, Object[], TargetSource) line: 476 
    InfrastructureAdvisorAutoProxyCreator(AbstractAutoProxyCreator).wrapIfNecessary(Object, String, Object) line: 362 
    InfrastructureAdvisorAutoProxyCreator(AbstractAutoProxyCreator).postProcessAfterInitialization(Object, String) line: 322  
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).applyBeanPostProcessorsAfterInitialization(Object, String) line: 407 
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).initializeBean(String, Object, RootBeanDefinition) line: 1461  
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 519 
    DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 456 
    AbstractBeanFactory$1.getObject() line: 294 
    DefaultListableBeanFactory(DefaultSingletonBeanRegistry).getSingleton(String, ObjectFactory) line: 225 
    DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 291  
    DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 193 
    DefaultListableBeanFactory.preInstantiateSingletons() line: 585 
    GenericApplicationContext(AbstractApplicationContext).finishBeanFactoryInitialization(ConfigurableListableBeanFactory) line: 913  
    GenericApplicationContext(AbstractApplicationContext).refresh() line: 464 
    GenericXmlContextLoader(AbstractGenericContextLoader).loadContext(MergedContextConfiguration) line: 103 
    GenericXmlContextLoader(AbstractGenericContextLoader).loadContext(MergedContextConfiguration) line: 1 
    DelegatingSmartContextLoader.loadContext(MergedContextConfiguration) line: 228 
    TestContext.loadApplicationContext() line: 124 
    TestContext.getApplicationContext() line: 148 
    DependencyInjectionTestExecutionListener.injectDependencies(TestContext) line: 109 
    DependencyInjectionTestExecutionListener.prepareTestInstance(TestContext) line: 75 
    TestContextManager.prepareTestInstance(Object) line: 321  
    SpringJUnit4ClassRunner.createTest() line: 211 
    SpringJUnit4ClassRunner$1.runReflectiveCall() line: 288 
    SpringJUnit4ClassRunner$1(ReflectiveCallable).run() line: 15  
    SpringJUnit4ClassRunner.methodBlock(FrameworkMethod) line: 290 
    SpringJUnit4ClassRunner.runChild(FrameworkMethod, RunNotifier) line: 231  
    SpringJUnit4ClassRunner(BlockJUnit4ClassRunner).runChild(Object, RunNotifier) line: 44 
    SpringJUnit4ClassRunner(ParentRunner<T>).runChildren(RunNotifier) line: 180 
    ParentRunner<T>.access$000(ParentRunner, RunNotifier) line: 41 
    ParentRunner$1.evaluate() line: 173 
    RunBefores.evaluate() line: 28 
    RunBeforeTestClassCallbacks.evaluate() line: 61 
    RunAfters.evaluate() line: 31 
    RunAfterTestClassCallbacks.evaluate() line: 71 
    SpringJUnit4ClassRunner(ParentRunner<T>).run(RunNotifier) line: 220 
    SpringJUnit4ClassRunner.run(RunNotifier) line: 174 
    JUnit4TestMethodReference(JUnit4TestReference).run(TestExecution) line: 50 
    TestExecution.run(ITestReference[]) line: 38  
    RemoteTestRunner.runTests(String[], String, TestExecution) line: 467  
    RemoteTestRunner.runTests(TestExecution) line: 683 
    RemoteTestRunner.run() line: 390  
    RemoteTestRunner.main(String[]) line: 197 

什麼是構造函數之所以被稱爲兩次,我怎樣才能防止這種情況發生?雖然這些單身檢查看起來好像在向單元測試環境拋出扳手,但在實際生產中,它們在過去很好地表現出了「硬」限制。

+0

在你所有的4個上下文文件中,這個類只有一個''聲明嗎? –

+0

@SotiriosDelimanolis是的,它只在其中一個上下文文件中定義。 –

+0

我無法重現您的問題。你能發表任何其他信息嗎?也許你的上下文或其他可能相關的'@ Test'情況。你在任何地方都有'@ DirtiesContext'嗎? –

回答

2

我會聲稱原因是你的單例類不暴露Spring可以用於其應用程序上下文的接口。 Spring基於其自動裝配來實現一個接口,該接口充當該類的單例代理。然而,由於你的singletonclass是一個實際的類,所以Spring不能這樣做,而是被迫使用cglib(你在堆棧跟蹤中看到的)子類化你的類。任何子類都必須調用超級構造函數,所以你會看到兩個調用。

因此,簡而言之:我認爲appcontext在這裏不是問題。如果你的類會暴露&實現一個Spring可以通過其代理實現的實際接口,就像推薦的那樣,我會懷疑你不會得到你的異常。

+0

這正是問題所在。謝謝! –

+0

@CraigOtis有些東西你沒有向我們展示過,因爲沒有理由Spring將代理你的實例作爲你的例子。 –

+0

@SotiriosDelimanolis你錯了。 Spring DI基於代理實例,它有兩個基本機制:實現一個接口,在代碼中提供的接口是不可能的,並且是子類化的。這就是Spring的工作原理。 – eis

相關問題