java Spring用非主bean覆盖主bean

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42564809/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 06:43:45  来源:igfitidea点击:

Spring overriding primary bean with non-primary bean

javaspringspring-bootdependency-injectionspring-test

提问by FiguringThisOut

I am trying to override a Spring bean during a test declared in a test configuration with the use of @Primary. One declaration is in the src/main/java path, the other, the primary, is in src/test/java path.

我试图在使用@Primary 在测试配置中声明的测试期间覆盖 Spring bean。一个声明在 src/main/java 路径中,另一个是主要的,在 src/test/java 路径中。

However, Spring is intentionally replacing the primary bean with the the non-primary bean, the one I don't want to use for the test. If I simply comment out the production (src/main/java) configuration bean, it uses the primary test (src/main/test) bean in the test configuration as desired. (Clearly I can't comment out code every time I want to run a test.)

但是,Spring 有意用非主 bean 替换主 bean,我不想将其用于测试。如果我只是注释掉生产 (src/main/java) 配置 bean,它会根据需要使用测试配置中的主要测试 (src/main/test) bean。(显然,每次我想运行测试时,我都无法注释掉代码。)

From the logs:

从日志:

o.s.b.f.s.DefaultListableBeanFactory - Overriding bean definition for bean 'sqsConnectionFactory' with a different definition: replacing[Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=true;factoryBeanName=testJmsConfiguration; factoryMethodName=sqsConnectionFactory; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/foo/configuration/TestJmsConfiguration.class]]

with

[Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=jmsConfiguration; factoryMethodName=sqsConnectionFactory; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/foo/configuration/JmsConfiguration.class]]

osbfsDefaultListableBeanFactory -用不同的定义覆盖 bean 'sqsConnectionFactory' 的 bean 定义:替换[Root bean: class [null]; 范围=; 抽象=假;懒惰初始化=假;autowireMode=3; 依赖检查=0;autowireCandidate=真;主要=真;factoryBeanName=testJmsConfiguration; factoryMethodName=sqsConnectionFactory; initMethodName=null; destroyMethodName=(推断);在类路径资源中定义 [com/foo/configuration/TestJmsConfiguration.class]]

[根bean:类[null]; 范围=; 抽象=假;懒惰初始化=假;autowireMode=3; 依赖检查=0;autowireCandidate=真;主要=假;factoryBeanName=jmsConfiguration; factoryMethodName=sqsConnectionFactory; initMethodName=null; destroyMethodName=(推断);在类路径资源中定义 [com/foo/configuration/JmsConfiguration.class]]

Why is spring replacing a primary bean with a non-primary bean and how do I get Spring to use the bean specifically marked as the primary bean?

为什么 spring 用非主 bean 替换主 bean,我如何让 Spring 使用专门标记为主 bean 的 bean?

Edit: The src/main/java configuration:

编辑:src/main/java 配置:

@Configuration
public class JmsConfiguration {

... other bean declarations here ...

@Bean
public SQSConnectionFactory sqsConnectionFactory(Region region) throws JMSException {
    return SQSConnectionFactory.builder()
            .withRegion(region)
            .build();
}
}

The test configuration:

测试配置:

@Configuration
public class TestJmsConfiguration {

@Bean(name="messageProducerMock")
public MessageProducer mockMessageProducer() {
    return new MessageProducerMock();
}

... other bean declarations here ...

@Bean
@Primary
public SQSConnectionFactory sqsConnectionFactory(@Qualifier("messageProducerMock") MessageProducer messageProducerMock) throws JMSException {
    ... returning setup mock here
}
}

The class with the tests is annotated with:

带有测试的类注释为:

@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles(profiles = {"test"})

采纳答案by alfcope

@Primarytakes effect only at injection point, when there is a conflict because different beans match the condition to be injected, and a decision needs to be made.

@Primary只在注入点生效,当因为不同的bean匹配要注入的条件而发生冲突时,需要做出决定。

@Primaryis not used at beans initialisation. As you are using two different methods creating the same bean, and you are not naming any of them Spring considers you are trying to override it, so this behaviour can happen. Given a name is the easiest solution, but bear in mind that your context will still be initialising the bean you do not want use.

@Primary不用于 bean 初始化。由于您使用两种不同的方法创建同一个 bean,并且您没有命名它们中的任何一个,Spring 认为您正在尝试覆盖它,因此这种行为可能会发生。给定名称是最简单的解决方案,但请记住,您的上下文仍将初始化您不想使用的 bean。

回答by hya

I think you might be missing @ContextConfigurationin your test class.

我想你可能会错过@ContextConfiguration你的测试课。

Example of test configuration class (src/test/java/TestConfiguration.class):

测试配置类示例(src/test/java/TestConfiguration.class):

@Configuration
@ComponentScan
public class TestConfiguration {
    @Bean
    RabbitSender rabbitSender() {
        return mock(RabbitSender.class);
    }

}

Example of test class:

测试类示例:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfiguration.class)
public class SomeServiceTest {

}