With Spring Boot 2.1 bean overriding is disabled by default, which is a good thing.
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.
The only way I found worked, was to enable bean overriding through application properties:
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.
The beans that I am overriding are either
What I was thinking should work in the test configuration overriding the bean and slap a @Primary
on it, as we are used to for data source configurations. This however has no effect and got me wondering: Is the @Primary
and the disabled bean overriding contradictory?
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); } }
Bean Overriding Spring beans are identified by their names within an ApplicationContext. Therefore, bean overriding is a default behavior that happens when we define a bean within an ApplicationContext that has the same name as another bean. It works by simply replacing the former bean in case of a name conflict.
When using @Bean without specifying name or alias, the default bean ID will be created based on the name of the method which was annotated with @Bean annotation. You can override this behaviour by specifying name or aliases for the bean. Alias is always the second name of the bean.
If you define two beans of same class, without different bean id or qualifiers ( identifier) , Spring container will not be able to understand which bean to be loaded , if you try to access the bean by passing the classname and you will get NoUniqueBeanDefinitionException as there are two qualifying TestBean.
If you use @Configuration, all methods marked as @Bean will be wrapped into a CGLIB wrapper which works as if it's the first call of this method, then the original method's body will be executed and the resulting object will be registered in the spring context.
spring.main.allow-bean-definition-overriding=true
can be placed in test configurations. If you need extensive integration testing, you will need to override beans at some point. It's inevitable.
Though the correct answer has already been provided, it implies that your bean will have different names. So, technically, it's not an override.
If you need a real override (because you use @Qualifiers
, @Resources
or something similar), since Spring Boot 2.X is only possible using the spring.main.allow-bean-definition-overriding=true
property.
Update: Be careful with Kotlin Bean Definition DSL. In Spring Boot it will require a custom ApplicationContextInitializer, like so:
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 @Bean
method, it will not do. The initializer will kick in after @Bean
methods and you'd still get the initial, DSL-based bean in your tests even with @Primary
on 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):
context: initializer: classes: com.yuranos.BeansInitializer, com.yuranos.TestBeansInitializer
Bean Definition DSL also supports primary property via:
bean(isPrimary=true) {...}
- which you'll need to eliminate ambiguity when you try to inject a bean, however main:allow-bean-definition-overriding: true
is not needed if you go pure DSL way.
(Spring Boot 2.1.3)
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:
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 @Primary
then primary bean will be injected by default in:
@Autowired BService bService;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With