2

AbstractJUnit4SpringContextTestsクラスに基づいた簡単な JUnit テストがあります。

@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" />

このシングルトンは、他のプロジェクト/場所で使用されているように、特定の時点でクラスのインスタンスが 1 つだけであることを保証するコンストラクターを持っています。

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

ただし、JUnit テストを実行すると、この例外が発生します。私はこの答えからそれを理解しました:junitテストクラス全体でSpringアプリケーションコンテキストを再利用する

アプリケーション コンテキストは、同じである限り再利用されましたlocations。しかし、そうではないようです。これらは、Bean がインスタンス化される 2 つの場所です。

初め:

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

2番:

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   

コンストラクターが 2 回呼び出される理由は何ですか?また、これを防ぐにはどうすればよいですか? これらのシングルトン チェックは、単体テスト環境に問題を投げかけているように見えますが、実際の運用環境では、過去に適切な「厳しい」制限として十分に機能していました。

4

1 に答える 1

2

その理由は、シングルトン クラスが、Spring がそのアプリケーション コンテキストに使用できるインターフェイスを公開していないためだと主張します。Spring は、クラスのシングルトン プロキシとして機能するインターフェイスの実装に基づいてオートワイヤリングを行います。ただし、singletonclass は実際のクラスであるため、Spring はそれを行うことはできませんが、代わりに cglib を使用してクラスをサブクラス化する必要があります (スタック トレースに表示されます)。どの子クラスもスーパー コンストラクターを呼び出す必要があるため、2 つの呼び出しが表示されます。

要するに、ここでは appcontext は問題ではないと思います。推奨されるように、クラスがSpringがプロキシで実装できる実際のインターフェースを公開および実装する場合、例外が発生しないと思われます。

于 2013-10-16T18:56:35.637 に答える