Spring @Autowired поведение отличается в тестах от компонентов
правила/поведение вокруг @Autowired
разные при написании тестов? Кажется, что с помощью теста вы можете autowire к конкретному типу, но если вы попробуете то же самое внутри @Component
это не удастся. Это надуманный пример, но это то, с чем я столкнулся, и я просто пытаюсь лучше понять.
надуманный пример кода:
public interface Gizmo {
void whirr();
}
@Configuration
public class GizmoConfiguration {
@Bean
@Profile("no-dependencies")
public Gizmo fooGizmoBean() {
return new FooGizmo();
}
@Bean
@Profile("!no-dependencies")
public Gizmo barGizmoBean() {
return new BarGizmo();
}
public class FooGizmo implements Gizmo {
@Override
public void whirr() {
}
}
public class BarGizmo implements Gizmo {
@Override
public void whirr() {
}
}
}
тест, который работает нормально:
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles(Application.Profiles.NO_DEPENDENCIES)
public class TestClass {
@Autowired
private GizmoConfiguration.FooGizmo gizmo;
@Test
public void test() {
assertNotNull(gizmo);
}
}
компонент, который вызывает java.lang.IllegalStateException: Failed to load ApplicationContext
:
@Component
public class TestComponent {
@Autowired
private GizmoConfiguration.FooGizmo gizmo;
}
потому что из:
No qualifying bean of type 'GizmoConfiguration$FooGizmo' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
1 ответов
правила / поведение вокруг @Autowired отличаются при написании тесты?
не совсем: правила на самом деле точно такие же. Разница заключается в сроках относительно того, как Spring определяет, является ли данный Боб кандидатом autowire.
кажется, что с помощью теста вы можете autowire к конкретному типу, но если вы попробуете то же самое внутри @Component, он потерпит неудачу.
I поймите, почему вы так думаете, поскольку ваш пример демонстрирует такое поведение, но ваш анализ не совсем корректен.
Итак, позвольте мне объяснить...
Когда весна пытается выполнить autowiring для вашего @Component
class, единственная информация, которую он имеет о типах (т. е. классах и интерфейсах) для бобов, поступающих из @Bean
методы-это информация, доступная в @Bean
формальная подпись метода.
в вашем примере, когда весна ищет так называемые "кандидаты autowire" для инъекции в ваш @Component
, Spring видит только Боб типа Gizmo
для fooGizmoBean()
@Bean
метод. Вот почему вы видите ошибку "нет квалификационного компонента типа" GizmoConfiguration$FooGizmo"", которая оказывается полностью правильной.
если вы хотите весной, чтобы иметь возможность autowire ваш @Component
используя конкретный тип, вам придется переопределить подпись вашего fooGizmoBean()
@Bean
метод, чтобы возвратить FooGizmo
вместо Gizmo
.
Итак, это первая половина истории. Вторая половина истории заключается в том, почему Spring Testcontext Framework может выполнять автозапуск по конкретному типу для тестового экземпляра.
причина, которая работает в том, что ApplicationContext
уже полностью запущен (т. е. все зерна были созданы и все @Bean
методы были вызваны контейнером) к тому времени, когда платформа тестирования пытается выполнить инъекцию зависимостей. Тем самым момент времени fooGizmoBean()
метод уже был вызван Spring, и Spring теперь знает, что конкретный тип на самом деле является FooGizmo
. Таким образом, @Autowired FooGizmo gizmo;
работает в тесте.