Spring boot 2.1 bean override vs. Primary

新手上路,请多包涵

使用 Spring Boot 2.1,默认情况下禁用 bean 覆盖,这是一件好事。

但是我确实进行了一些测试,其中我使用 Mockito 将 beans 替换为模拟实例。使用默认设置,具有此类配置的测试将由于 bean 覆盖而失败。

我发现唯一可行的方法是通过应用程序属性启用 bean 覆盖:

 spring.main.allow-bean-definition-overriding=true

但是,我真的很想确保为我的测试配置设置最小的 bean 定义设置,spring 会在禁用覆盖的情况下指出这一点。

我要覆盖的豆是

  • 在导入到我的测试配置中的另一个配置中定义
  • 通过注解扫描自动发现的bean

我的想法应该在覆盖 bean 的测试配置中起作用,并在其上打一个 @Primary ,就像我们习惯于数据源配置一样。然而,这没有任何效果,让我想知道: @Primary 和禁用的 bean 是否相互矛盾?

一些例子:

 package com.stackoverflow.foo;
@Service
public class AService {
}

package com.stackoverflow.foo;
public class BService {
}

package com.stackoverflow.foo;
@Configuration
public BaseConfiguration {
    @Bean
    @Lazy
    public BService bService() {
        return new BService();
    }
}

package com.stackoverflow.bar;
@Configuration
@Import({BaseConfiguration.class})
public class TestConfiguration {
    @Bean
    public BService bService() {
        return Mockito.mock(BService.class);
    }
}

原文由 hotzst 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 622
2 个回答

覆盖 beans 意味着上下文中可能只有一个具有唯一名称或 id 的 bean。所以你可以通过以下方式提供两个bean:

 package com.stackoverflow.foo;
@Configuration
public class BaseConfiguration {
   @Bean
   @Lazy
   public BService bService1() {
       return new BService();
   }
}

package com.stackoverflow.bar;
@Configuration
@Import({BaseConfiguration.class})
public class TestConfiguration {
    @Bean
    public BService bService2() {
        return Mockito.mock(BService.class);
    }
}

如果添加 @Primary 则主 bean 将默认注入到:

 @Autowired
BService bService;

原文由 Alien11689 发布,翻译遵循 CC BY-SA 4.0 许可协议

spring.main.allow-bean-definition-overriding=true 可以放在测试配置中。如果您需要广泛的集成测试,您将需要在某些时候覆盖 beans。这是不可避免的。

虽然已经提供了正确答案,但这意味着您的 bean 将具有不同的名称。因此,从技术上讲,这不是覆盖。

如果您需要真正的覆盖(因为您使用 @Qualifiers@Resources 或类似的东西),因为 Spring Boot 2.X 只能使用 spring.main.allow-bean-definition-overriding=true

更新: 小心 Kotlin Bean Definition DSL。在 Spring Boot 中,它将需要一个自定义的 ApplicationContextInitializer,如下所示:

 class BeansInitializer : ApplicationContextInitializer<GenericApplicationContext> {

    override fun initialize(context: GenericApplicationContext) =
            beans.initialize(context)

}

现在,如果您决定通过 @Primary @Bean 方法在您的测试中覆盖其中一个基于 DSL 的 bean,它不会执行。初始化程序将在 @Bean 方法之后启动,即使在测试中使用 @Primary @Bean .另一种选择是还为您的测试创建一个测试初始值设定项,并将它们全部列在您的测试属性中,就像这样(顺序很重要):

 context:
    initializer:
        classes: com.yuranos.BeansInitializer, com.yuranos.TestBeansInitializer

Bean Definition DSL 还通过以下方式支持主属性:

 bean(isPrimary=true) {...}

- 当您尝试注入 bean 时,您需要消除歧义, 但是如果您采用纯 DSL 方式,则不需要 main:allow-bean-definition-overriding: true

(弹簧引导 2.1.3)

原文由 yuranos 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题