Java Spring Boot 2.1 bean 覆盖与主要

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/53139244/
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-08-11 00:43:47  来源:igfitidea点击:

Spring boot 2.1 bean override vs. Primary

javaspringspring-boot

提问by hotzst

With Spring Boot 2.1 bean overriding is disabledby default, which is a good thing.

随着春季启动2.1豆覆盖被禁用默认情况下,这是一件好事。

However I do have some tests where I replace beans with mocked instances using Mockito. With the default setting Tests with such a configuration will fail due to bean overriding.

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

The only way I found worked, was to enable bean overriding through application properties:

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

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

However I would really like to ensure minimal bean definition setup for my test configuration, which would be pointed out by spring with the overriding disabled.

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

The beans that I am overriding are either

我覆盖的 bean 要么是

  • Defined in another configuration that imported into my test configuration
  • Auto-discovered bean by annotation scanning
  • 在导入到我的测试配置中的另一个配置中定义
  • 通过注解扫描自动发现bean

What I was thinking should work in the test configuration overriding the bean and slap a @Primaryon it, as we are used to for data source configurations. This however has no effect and got me wondering: Is the @Primaryand the disabled bean overriding contradictory?

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

Some example:

一些例子:

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);
    }
}

采纳答案by Alien11689

Overriding beans means that there may be only one bean with a unique name or id in the context. So you can provide two beans in the following way:

覆盖 bean 意味着在上下文中可能只有一个具有唯一名称或 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);
    }
}

If you add @Primarythen primary bean will be injected by default in:

如果添加,@Primary则默认情况下将注入主 bean:

@Autowired
BService bService;

回答by Pavel

It is allowed to override @Component with @Bean by default. In your case

默认情况下允许使用@Bean 覆盖@Component。在你的情况下

@Service
public class AService {
}

@Component
public class BService {
    @Autowired
    public BService() { ... }
}

@Configuration
@ComponentScan
public BaseConfiguration {
}

@Configuration
// WARNING! Doesn't work with @SpringBootTest annotation
@Import({BaseConfiguration.class})
public class TestConfiguration {
    @Bean // you allowed to override @Component with @Bean.
    public BService bService() {
        return Mockito.mock(BService.class);
    }
}

回答by yuranos

spring.main.allow-bean-definition-overriding=truecan be placed in test configurations. If you need extensive integration testing, you will need to override beans at some point. It's inevitable.

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

Though the correct answer has already been provided, it implies that your bean will have different names. So, technically, it's not an override.

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

If you need a real override (because you use @Qualifiers, @Resourcesor something similar), since Spring Boot 2.X is only possible using the spring.main.allow-bean-definition-overriding=trueproperty.

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

Update:Be careful with Kotlin Bean Definition DSL. In Spring Boot it will require a custom ApplicationContextInitializer, like so:

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

class BeansInitializer : ApplicationContextInitializer<GenericApplicationContext> {

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

}

Now if you decide to override one of such DSL-based beans in your test via @Primary @Beanmethod, it will not do. The initializer will kick in after @Beanmethods and you'd still get the initial, DSL-based bean in your tests even with @Primaryon the test @Bean. One other option would be to also create a test initializer for your tests and list them all in your test properties, like so(order matters):

现在,如果您决定通过@Primary @Bean方法在测试中覆盖此类基于 DSL 的 bean 之一,则不会这样做。初始化程序将在@Bean方法之后启动,即使@Primary在 test 中,您仍然会在测试中获得初始的、基于 DSL 的 bean @Bean。另一种选择是为您的测试创建一个测试初始值设定项,并将它们全部列在您的测试属性中,如下所示(顺序很重要):

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

Bean Definition DSL also supports primary property via:

Bean 定义 DSL 还通过以下方式支持主要属性:

bean(isPrimary=true) {...}

- which you'll need to eliminate ambiguity when you try to inject a bean, however main:allow-bean-definition-overriding: trueis not needed if you go pure DSL way.

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

(Spring Boot 2.1.3)

(Spring Boot 2.1.3)

回答by daltonfury42

I make the testing beans available only in testprofile, and allow overriding for just while testing, like this:

我使测试 bean 仅在test配置文件中可用,并允许在测试时覆盖,如下所示:

@ActiveProfiles("test")
@SpringBootTest(properties = {"spring.main.allow-bean-definition-overriding=true"})
class FooBarApplicationTests {

  @Test
  void contextLoads() {}
}

The bean I am mocking in the test configuration:

我在测试配置中嘲笑的 bean:

@Profile("test")
@Configuration
public class FooBarApplicationTestConfiguration {
  @Bean
  @Primary
  public SomeBean someBean() {
    return Mockito.mock(SomeBean.class);
  }
}