@ComponentScan в @SpringBootApplication. Аннотация @Import

При запуске 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 находится в корневом пакете. А значит то по умолчанию сканируются все классы, которые находятся ниже.

Важно не размещать главный класс в так называемый default package, то есть в совсем корневой пакет без названия, поскольку в этом случае будет сканироваться весь classpath с библиотеками и приложение не запустится.

То есть в пакете ru.sysout MainApplication может находиться, а сразу в папке java без пакета — нет.

Корректная структура:

Так - нормально
Так — нормально

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

Пакеты, параллельные пакету с главным классом @SpringBootApplication, не сканируются

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

Здесь car параллелен ru.sysout с главным классом
Здесь пакет car параллелен пакету ru.sysout с главным классом

Теперь 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.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *