spring - 升级到 4.1.4.RELEASE 后,在上下文创建时错误地创建了原型(prototype)范围的 Spring bean

标签 spring junit

我在测试配置中定义了一个原型(prototype)作用域 bean,需要使用 ApplicationContext (applicationContext.getBean("my-failing-prototype-bean", myStringParam) 创建它,因为它有一个需要传递的字符串参数。

当我使用 Spring 4.0.4 时,当我运行测试时,它工作得很好。当我运行测试类时会加载上下文,并且直到需要时才创建原型(prototype)作用域 bean。现在,加载上下文的过程尝试创建 bean 的实例,但失败了,因为没有合适的 bean 与我需要手动传递给 bean 的工厂方法的 String 参数相匹配。

单步完成上下文创建过程后,似乎在尝试 Autowiring 不同的 bean 时,Spring 正在调用 DefaultListableBeanFactory.getBeanNamesForType(),后者又调用 AbstraceAutowireCapableBeanFactory.getNonSingletonFactoryBeanForTypeCheck() code> 然后调用 AbstraceAutowireCapableBeanFactory.createBeanInstance() 因为 AbstraceAutowireCapableBeanFactory.resolveBeforeInstantiation() 返回 null。

编辑:将我的项目提炼为产生问题的示例项目后,我发现只有当我的测试 Spring 配置中声明的 bean 实现 FactoryBeanBeanFactoryAware,或BeanClassLoaderAware。它也肯定只发生在有另一个 bean 注入(inject)到测试 Spring 配置中时。

感谢您的帮助,如果您需要更多详细信息,请告诉我!

GitHub repository for a simple project that reproduces this issue

TestSpringConfig.java

@Configuration
public class TestSpringConfig {

    @Inject
    @Named("my-other-bean")
    MyOtherBean myOtherBean;

    @Inject
    private ApplicationContext applicationContext;

    @Bean(name = {"my-failing-prototype-bean"})
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    @Profile("test")
    public JndiObjectFactoryBean myFailingPrototypeBean(final String jndiName) {
        JndiObjectFactoryBean jof = new JndiObjectFactoryBean();
        JndiTemplate testJndiTemplate = new JndiTemplate();

        jof.setResourceRef(true);
        jof.setJndiTemplate(testJndiTemplate);
        jof.setJndiName(jndiNameForQueue);

        return jof;
    }

MyTest.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestSpringConfig.class, loader = AnnotationConfigContextLoader.class)
@ActiveProfiles("test")
public class MyTest {

    @Test
    public void runTest() { ...

堆栈跟踪

java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:94)
    at org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:72)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:212)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:200)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:252)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:254)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:217)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'TestSpringConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.me.MyOtherBean com.me.TestSpringConfig.myOtherBean; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'my-failing-prototype-bean' defined in com.me.TestSpringConfig: Unsatisfied dependency expressed through constructor argument with index 0 of type [java.lang.String]: : No qualifying bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1202)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:762)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:125)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
    at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:68)
    at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:86)
    ... 28 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.me.MyOtherBean com.me.TestSpringConfig.myOtherBean; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'my-failing-prototype-bean' defined in com.me.TestSpringConfig: Unsatisfied dependency expressed through constructor argument with index 0 of type [java.lang.String]: : No qualifying bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
    ... 42 more
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'my-failing-prototype-bean' defined in com.me.TestSpringConfig: Unsatisfied dependency expressed through constructor argument with index 0 of type [java.lang.String]: : No qualifying bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:464)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1111)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1006)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getNonSingletonFactoryBeanForTypeCheck(AbstractAutowireCapableBeanFactory.java:896)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:791)
    at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:542)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:436)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:412)
    at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:187)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1112)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1051)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:949)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
    ... 44 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1308)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1054)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:949)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
    ... 57 more

最佳答案

据我所知,将 bean 作为原型(prototype)并不能阻止它被急切地初始化。尝试向您的 bean 添加 @Lazy 注释。

@Bean(name = {"my-failing-prototype-bean"})
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    @Profile("test")
    @Lazy
    public JndiObjectFactoryBean myFailingPrototypeBean(final String jndiName) {
        JndiObjectFactoryBean jof = new JndiObjectFactoryBean();
        JndiTemplate testJndiTemplate = new JndiTemplate();

        jof.setResourceRef(true);
        jof.setJndiTemplate(testJndiTemplate);
        jof.setJndiName(jndiNameForQueue);

        return jof;
    }

关于spring - 升级到 4.1.4.RELEASE 后,在上下文创建时错误地创建了原型(prototype)范围的 Spring bean,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28859716/

相关文章:

java - CORS 过滤器出现问题

spring - 如何在 spring boot 2.1.9 中配置 ssl 证书

java - 如何让 Eclipse 始终首选其他库包含的库的最新版本?

java - JUnit:在多个目标类上运行相同的测试

junit - DbUnit - 基于非主键字段值删除行

java - 为什么我的存储过程仅返回单个整数返回代码,但堆空间不足?

java - 使用空列表作为输入调用 Itemwriter

spring - Kotlin 的 Arrow <Exception, X> 和交易

arrays - 如何使用 assertArrayEquals 比较两个对象数组

java - ModelMapper JUnit Mockito 抛出 NullPointerException