Перейти к основному содержимому

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

· 5 мин. чтения

Задача: Медиана двух отсортированных массивов

Даны два отсортированных массива размерами n и m. Найдите медиану слияния этих двух массивов.
Временная сложность решения должна быть O(log(m + n)) ...

ANDROMEDA

1. Обзор

В этом руководстве мы рассмотрим сканирование компонентов в Spring. При работе с Spring мы можем аннотировать наши классы, чтобы превратить их в bean-компоненты Spring. Кроме того, мы можем указать Spring, где искать эти аннотированные классы, так как не все они должны стать bean-компонентами в этом конкретном запуске.

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

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

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

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

В Spring мы используем аннотацию @ComponentScan вместе с аннотацией @Configuration , чтобы указать пакеты, которые мы хотим сканировать . @ComponentScan без аргументов указывает Spring сканировать текущий пакет и все его подпакеты.

Допустим, у нас есть следующая конфигурация @Configuration в пакете com.foreach.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.foreach.componentscan.springapp.animals :

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

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

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

Вывод метода main() будет содержать все bean- компоненты пакета com.foreach.componentscan.springapp и его подпакетов:

springComponentScanApp
cat
dog
rose
exampleBean

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

Следует также отметить, что основной класс приложения и класс конфигурации не обязательно совпадают. Если они разные, неважно, куда мы поместим основной класс приложения. Имеет значение только расположение класса конфигурации, так как сканирование компонента по умолчанию начинается с его пакета .

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

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

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

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

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

@Configuration
@EnableAutoConfiguration
@ComponentScan

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

package com.foreach.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.foreach.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 с аргументами

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

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

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

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

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

springComponentScanApp
cat
dog
exampleBean

Давайте посмотрим, что за этим стоит:

  • springComponentScanApp создается, поскольку его конфигурация передается в качестве аргумента в AnnotationConfigApplicationContext .
  • exampleBean — это bean-компонент, настроенный внутри конфигурации.
  • кошка и собака находятся в указанном пакете com.foreach.componentscan.springapp.animals

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

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

3.2. @ComponentScan с несколькими пакетами

Spring предоставляет удобный способ указать несколько имен пакетов. Для этого нам нужно использовать массив строк.

Каждая строка массива обозначает имя пакета:

@ComponentScan(basePackages = {"com.foreach.componentscan.springapp.animals", "com.foreach.componentscan.springapp.flowers"})

В качестве альтернативы, начиная с весны 4.1.1, мы можем использовать запятую, точку с запятой или пробел для разделения списка пакетов :

@ComponentScan(basePackages = "com.foreach.componentscan.springapp.animals;com.foreach.componentscan.springapp.flowers")
@ComponentScan(basePackages = "com.foreach.componentscan.springapp.animals,com.foreach.componentscan.springapp.flowers")
@ComponentScan(basePackages = "com.foreach.componentscan.springapp.animals com.foreach.componentscan.springapp.flowers")

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

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

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

Мы также можем выбрать другой тип фильтра, так как аннотация поддерживает несколько гибких опций для фильтрации сканируемых классов :

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

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

Нам следует избегать включения класса @Configuration в пакет по умолчанию (т. е. вообще не указывать пакет). Если мы это сделаем, Spring просканирует все классы во всех jar-файлах в пути к классам, что вызовет ошибки и приложение, вероятно, не запустится.

5. Вывод

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

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