При запуске Spring-приложения пакеты сканируются. Из аннотированных с помощью @Component, @Service, @Repository и @Configuration клаccов создаются бины. В этой статье рассмотрим, как задаются пакеты и классы для сканирования.
@ComponentScan в @SpringBootApplication
Сканирование происходит благодаря аннотации @ComponentScan. В Sping Boot приложении она явно не прописывается, но содержится внутри аннотации @SpringBootApplication, с помощью которой аннотирован главный класс приложения.
Аннотация @SpringBootApplication:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
Сканируются все классы, которые находятся в том же пакете, что и класс с @ComponentScan (то есть с @SpringBootApplication), а также в подпакетах этого пакета.
Если сгенерировать Spring Boot проект на сайте https://start.spring.io, то видно: главный класс с аннотацией с @SpringBootApplication находится в корневом пакете. А значит то по умолчанию сканируются все классы, которые находятся ниже.
То есть в пакете ru.sysout MainApplication может находиться, а сразу в папке java без пакета — нет.
Корректная структура:

Классы Car и Engine у нас компоненты — они аннотированы с помощью @Component. Поскольку они лежат в структуре пакетов ниже главного класса, они успешно сканируются и на их основе создаются бины.
Пакеты, параллельные пакету с главным классом @SpringBootApplication, не сканируются
Но ситуации бывают разные. Попробуем переложить Car в другой пакет, который находится не в подпакете относительно MainApplication, а параллельно ему.

Теперь Car не сканируется и бин не создается. В этом можно убедиться, если попытаться извлечь бин типа Car из ApplicationContext:
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
ApplicationContext applicationContext=SpringApplication.run(MainApplication.class, args);
Car car=applicationContext.getBean(Car.class);
}
}
В консоли появляется ошибка:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'car.Car' available
И хотя такая структура пакетов не является стандартной, ошибку можно исправить с помощью @Import.
Аннотация @Import
С помощью @Import можно добавить классы в перечень сканируемых, даже если они лежат в другом пакете.
Чтобы просканировать Car, рядом с @SpringBootApplication добавим аннотацию @Import:
@SpringBootApplication
@Import({Car.class})
public class MainApplication {
public static void main(String[] args) {
ApplicationContext applicationContext=SpringApplication.run(MainApplication.class, args);
Car car=applicationContext.getBean(Car.class);
}
}
В @Import можно передать массив классов, но в нашем примере достаточно одного Car.
Исходный код
Код примера есть на GitHub.