Сканирование компонентов Spring

Сканирование пружинных компонентов

1. обзор

В этом руководстве мы рассмотрим сканирование компонентов в Spring. При работе с Spring мы можем аннотировать наши классы, чтобы превратить их в бины Spring. Но, помимо этого,we can tell Spring where to search for these annotated classes, поскольку не все из них, должны стать beans в этом конкретном запуске.

Конечно, есть некоторые значения по умолчанию для сканирования компонентов, но мы также можем настроить пакеты для поиска.

Во-первых, давайте посмотрим на настройки по умолчанию.

2. @ComponentScan без аргументов

2.1. Использование@ComponentScan в приложении Spring

С Springwe use the @ComponentScan annotation along with @Configuration annotation to specify the packages that we want to be scanned. @ComponentScan без аргументов указывает Spring сканировать текущий пакет и все его подпакеты.

Допустим, у нас есть следующий@Configuration в пакетеcom.example.componentscan.springapp:

@Configuration
@ComponentScan
public class SpringComponentScanApp {
    private static ApplicationContext applicationContext;

    @Bean
    public ExampleBean exampleBean() {
        return new ExampleBean();
    }

    public static void main(String[] args) {
        applicationContext =
          new AnnotationConfigApplicationContext(SpringComponentScanApp.class);

        for (String beanName : applicationContext.getBeanDefinitionNames()) {
            System.out.println(beanName);
        }
    }
}

Также предположим, что у нас есть компонентыCat иDog в пакетеcom.example.componentscan.springapp.animals:

package com.example.componentscan.springapp.animals;
// ...
@Component
public class Cat {}
package com.example.componentscan.springapp.animals;
// ...
@Component
public class Dog {}

И, наконец, у нас есть компонентRose в пакетеcom.example.componentscan.springapp.flowers:

package com.example.componentscan.springapp.flowers;
// ...
@Component
public class Rose {}

Результат методаmain() будет содержать все компоненты пакетаcom.example.componentscan.springapp и его подпакетов:

springComponentScanApp
cat
dog
rose
exampleBean

Обратите внимание, что основной класс приложения также является bean-компонентом, поскольку он аннотирован@Configuration,, который является@Component.

Также обратите внимание, что основной класс приложения и класс конфигурации не обязательно совпадают. Если они разные, не имеет значения, где разместить основной класс приложения. Only the location of the configuration class matters as component scanning starts from its package by default.

Наконец, обратите внимание, что в нашем примере@ComponentScan эквивалентен:

@ComponentScan(basePackages = "com.example.componentscan.springapp")

где аргументbasePackages - это пакет или массив пакетов для сканирования.

2.2. Использование@ComponentScan в приложении Spring Boot

Хитрость Spring Boot в том, что многие вещи происходят неявно. Мы используем аннотацию@SpringBootApplication, но это всего лишь комбинация трех аннотаций:

@Configuration
@EnableAutoConfiguration
@ComponentScan

Создадим аналогичную структуру в пакетеcom.example.componentscan.springbootapp. На этот раз основным приложением будет:

package com.example.componentscan.springbootapp;
// ...
@SpringBootApplication
public class SpringBootComponentScanApp {
    private static ApplicationContext applicationContext;

    @Bean
    public ExampleBean exampleBean() {
        return new ExampleBean();
    }

    public static void main(String[] args) {
        applicationContext = SpringApplication.run(SpringBootComponentScanApp.class, args);
        checkBeansPresence(
          "cat", "dog", "rose", "exampleBean", "springBootComponentScanApp");

    }

    private static void checkBeansPresence(String... beans) {
        for (String beanName : beans) {
            System.out.println("Is " + beanName + " in ApplicationContext: " +
              applicationContext.containsBean(beanName));
        }
    }
}

Все остальные пакеты и классы остаются прежними, мы просто скопируем их в ближайший пакетcom.example.componentscan.springbootapp.

Spring Boot сканирует пакеты аналогично нашему предыдущему примеру. Давайте проверим вывод:

Is cat in ApplicationContext: true
Is dog in ApplicationContext: true
Is rose in ApplicationContext: true
Is exampleBean in ApplicationContext: true
Is springBootComponentScanApp in ApplicationContext: true

Причина, по которой мы просто проверяем наличие bean-компонентов во втором примере (а не выводим на печать все bean-компоненты), заключается в том, что вывод будет слишком большим.

Это из-за неявной аннотации@EnableAutoConfiguration, которая заставляет Spring Boot автоматически создавать множество bean-компонентов, полагаясь на зависимости в файлеpom.xml.

3. @ComponentScan с аргументами

Теперь давайте настроим пути для сканирования. Например, предположим, что мы хотим исключить bean-компонентRose.

3.1. @ComponentScan для конкретных пакетов

Мы можем сделать это несколькими способами. Во-первых, мы можем изменить базовый пакет:

@ComponentScan(basePackages = "com.example.componentscan.springapp.animals")
@Configuration
public class SpringComponentScanApp {
   // ...
}

Теперь вывод будет:

springComponentScanApp
cat
dog
exampleBean

Посмотрим, что за этим стоит:

  • springComponentScanApp создается, поскольку это конфигурация, переданная в качестве аргументаAnnotationConfigApplicationContext

  • exampleBean - это bean-компонент, настроенный внутри конфигурации

  • cat иdog находятся в указанном пакетеcom.example.componentscan.springapp.animals

Все вышеперечисленные настройки применимы и в Spring Boot. Мы можем использовать@ComponentScan вместе с@SpringBootApplication, и результат будет таким же:

@SpringBootApplication
@ComponentScan(basePackages = "com.example.componentscan.springbootapp.animals")

3.2. @ComponentScan с исключениями

Другой способ - использовать фильтр, определяющий шаблон для исключаемых классов:

@ComponentScan(excludeFilters =
  @ComponentScan.Filter(type=FilterType.REGEX,
    pattern="com\\.example\\.componentscan\\.springapp\\.flowers\\..*"))

Мы также можем выбрать другой тип фильтра, напримерthe annotation supports several flexible options for filtering the scanned classes:

@ComponentScan(excludeFilters =
  @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = Rose.class))

4. Пакет по умолчанию

Мы должны избегать размещения класса@Configurationin the default package (т.е. не указав пакет вообще). В этом случае Spring сканирует все классы во всех банках в пути к классам. Это вызывает ошибки, и приложение, вероятно, не запускается.

5. Заключение

В этой статье мы узнали, какие пакеты Spring сканирует по умолчанию и как настроить эти пути.

Как обычно, доступен полный кодover on GitHub.