我有一个简单的JUnit测试,基于AbstractJUnit4SpringContextTests
类:
@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" />
这个Singleton,因为它在其他项目/位置中使用,有一个构造函数,确保在给定时间只有一个类的实例:
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 :(得分:2)
我认为原因是你的单例类没有公开Spring可以用于其应用程序上下文的接口。 Spring将其自动装配基于实现一个充当该类的单例代理的接口。但是,由于你的singletonclass是一个实际的类,Spring不能这样做,而是被迫使用cglib(你在堆栈跟踪中看到)对你的类进行子类化。任何子类都必须调用超级构造函数,因此您会看到两个调用。
因此,简而言之:我认为appcontext不是问题所在。如果你的课程会曝光&amp;实现一个Spring可以用它的代理实现的实际接口,就像推荐的那样,我怀疑你不会得到你的异常。