Spring Boot 2.1 переопределение бина против основного
В Spring Boot 2.1 переопределение bean-компонентов отключено по умолчанию, и это хорошо.
Однако у меня есть несколько тестов, где я заменяю bean-компоненты на mockito. При настройке по умолчанию тесты с такой конфигурацией не будут выполнены из-за переопределения бина.
Единственный способ, который я нашел работающим, состоял в том, чтобы включить переопределение bean-компонентов через свойства приложения:
spring.main.allow-bean-definition-overriding=true
Однако я действительно хотел бы обеспечить минимальную настройку определения bean-компонента для моей тестовой конфигурации, которая будет указана spring с отключенной переопределением.
Бобы, которые я переопределяю, либо
- Определено в другой конфигурации, которая импортирована в мою тестовую конфигурацию
- Автоматически обнаруженный бин путем сканирования аннотаций
То, о чем я думал, должно работать в тестовой конфигурации, переопределяя bean-компонент и @Primary
к нему, как мы привыкли для конфигураций источника данных. Это, однако, не имеет никакого эффекта и @Primary
меня задуматься: противоречиво ли переопределение бина @Primary
и отключенного бина?
Пример:
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);
}
}
Ответы
Ответ 1
Переопределение 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
основной бин будет добавлен по умолчанию в:
@Autowired
BService bService;
Ответ 2
По умолчанию разрешено переопределять @Component с помощью @Bean. В твоем случае
@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);
}
}
Ответ 3
spring.main.allow-bean-definition-overriding=true
может быть помещен в тестовые конфигурации. Если вам нужно обширное интеграционное тестирование, вам придется переопределить bean-компоненты в какой-то момент. Это неизбежно.
Я просто хотел еще раз подчеркнуть, что, хотя правильный ответ уже предоставлен, это означает, что у вашего компонента будут разные имена, так что технически это не переопределение. Реальное переопределение, если оно вам нужно, потому что вы используете @Qualifiers
, @Resources
или что-то подобное, начиная с Spring Boot 2.X, возможно только с spring.main.allow-bean-definition-overriding=true
Обновление: будьте осторожны с DSL определения Kotlin Bean. В Spring Boot для этого потребуется специальный ApplicationContextInitializer, например:
class BeansInitializer : ApplicationContextInitializer<GenericApplicationContext> {
override fun initialize(context: GenericApplicationContext) =
beans.initialize(context)
}
Теперь, если вы решите переопределить один из таких компонентов на основе DSL в своем тесте с помощью метода @Primary @Bean
, это не сработает. Инициализатор включится после методов @Bean
и вы все равно получите начальный bean-компонент на основе DSL в своих тестах даже с @Primary
в тесте @Bean
. Еще один вариант - создать инициализатор теста для ваших тестов и перечислить их все в свойствах теста, например, так (порядок имеет значение):
context:
initializer:
classes: com.yuranos.BeansInitializer, com.yuranos.TestBeansInitializer
Определение Bean DSL также поддерживает первичное свойство через:
bean(isPrimary=true) {...}
- что вам понадобится, чтобы устранить неоднозначность, когда вы попытаетесь внедрить бин, однако main:allow-bean-definition-overriding: true
не нужен, если вы идете чистым способом DSL.
(Spring Boot 2.1.3)