Это не работает, потому что у вас есть круговая зависимость в вашем приложении. Когда контекст Spring загружает все bean-компоненты, он пытается создать bean-компоненты в порядке, необходимом для их полной работы. В этом случае, когда создается EngineConfiguration
, он пытается внедрить все типы bean-компонентов IEngine
, потому что вы объявили в конструкторе как зависимости, но в том же классе вы объявляете их.
Вы можете решить свою проблему, объявив компонент IEngine
вне класса EngineConfiguration
.
Вы можете прочитать об этом здесь .
ОБНОВЛЕНО для решения проблем с вашими тестами.
Кажется, что org.springframework.boot.test.context.SpringBootTest#classes
не поддерживает интерфейсы. Вам нужно указать либо классы вашей реализации Engine, либо создать макет интерфейса IEngine
.
Опция # 1: внедрить реальные реализации и запросить несуществующий тип.
@SpringBootTest(classes = {EngineFactory.class, EngineOne.class, EngineTwo.class}, properties = {
"someField=someValue",
"myObject.type=engineOneType",
"myObject.someOtherField=someOtherValue"
})
class EngineFactoryTest {
@Autowired
EngineFactory engineFactory;
@Test
void invalidEngineType() {
assertThatExceptionOfType(EngineNotSupportedException.class).isThrownBy(() -> {
engineFactory.getEngine("engineThreeType"); //non-existent type
});
}
}
Опция # 2: вам не нужна реальная реализация IEngine.
@SpringBootTest(classes = {EngineFactory.class}, properties = {
"someField=someValue",
"myObject.type=engineOneType",
"myObject.someOtherField=someOtherValue"
})
class EngineFactoryTest {
@Autowired
EngineFactory engineFactory;
@MockBean(name = "EngineThree")
private IEngine mockEngine;
@Test
void invalidEngineType() {
Mockito.when(mockEngine.getType()).thenReturn("engineThree"); //mock the getType method with any value.
assertThatExceptionOfType(EngineNotSupportedException.class).isThrownBy(() -> {
engineFactory.getEngine("engineOneType");
});
}
}
Опция # 3 Поскольку вы используете конфигурацию свойств, чтобы определить, какой движок использовать, я бы предпочел этого не делать загрузить в память неиспользуемый экземпляр IEngine. Я бы использовал @ConditionalOnProperty
@ConditionalOnProperty(
name = "myObject.type",
havingValue = "EngineOne")
public class EngineOne implements IEngine {
public String getType() {
return "EngineOne";
}
}
@Component("engineTwoType")
@ConditionalOnProperty(
name = "myObject.type",
havingValue = "EngineTwo")
public class EngineTwo implements IEngine {
public String getType() {
return "EngineTwo";
}
}